jQuery不得不说是一个越用越喜爱的js框架,好处就太多了,google一下一大把,而jQuery自身提供的方法是有限的,对于要完成一个特定的复杂功能,就要自己去编写一些插件,对于这些插件具有高度的独立性和可复用性,在项目开发总可以节省大量的时间和精力,下面就把我学习jQuery Plugin的一些笔记分享给大家,如有写错的地方大家可要不吝赐教



一、搭建jQueryPlugin最简单的骨架,在jQuery的prototype上增加函数($.fn==$.prototype),下面的写法也算是固定写法

jQuery.fn.myPlugin = function() {
  // Do your awesome plugin stuff here
}



二、上面已经完成了jQueryPlugin最基本的骨架,在里面写一些自己的逻辑就可以实现一些功能,但这还没完,对于引入多个插件就会有与其他插件发生命名冲突的危险,虽然几率很小,如果遇上了也是很麻烦的,这一节就是将jQueryPlugin放在一个自己执行的范围内,避免与其他js函数发生冲突,用到的方法是js的闭包(closure,具体闭包也理解的不太清楚,指导的大牛可以给点指点哈),所以对于上面的插件代码就可以在进行优化一下

<scripttype="text/javascript">
	// usejs closure
	(function($){
		$.fn.myPlugin=function(){
			// add a plugin code
		};
	})(jQuery);
</script>



三、插件执行的上下文(Context)就是自己所写插件的执行范围,个人理解就是$.fn.myPlugin=function(){}这个函数的执行范围,在这个函数执行范围中this关键字是指向jQuery对象的,所以this关键字可以直接引用jQuery的方法,不必使用$(this)进行包装


<scripttype="text/javascript">
	// use js closure
	(function($){
		$.fn.myPlugin=function(){
			// find方法没有报错说明是一个jQuery的对象
			var root = this.find(‘root’) ;
		};
	})(jQuery);
</script>



四、维持jQuery的链式操作jQuery的一个让人爱不释手的特点是其进行的链式操作,为了继续维持这种链式操作,在我们的操作中时刻记得返回this关键字。


(function($){
		$.fn.lockDimension = function(type){
			// 进行链式的传递
			return this.each(function(){
				// 当前循环的链节点
				var $this = $(this) ;
				if(!type||type=='width'){
					$this.width($this.width()) ;
				}
				if(!type||type=='height'){
					$this.height($this.width()) ;
				}
			}) ;
		} ;
	})(jQuery) ;




五、为插件方法设定默认值,对于一个插件方法可能需要传入很多参数,对于该插件方法在调用处对每个参数都进行设置,这将是一个很麻烦的事,解决的方案是可以事先设置好一些最优的默认值,如果需要对其修改可以使用对象字面量的方式进行复写,这样更简单快捷,对于现在的一个jqueryeaseui的用法基本上方法的调用都采用了这种方式,采取的是在$.extend,例子

(function($){
		$.fn.toolTip = function(opt){
			// 创建默认值,包含在指定插件方法之中,使用给定的opt进行复写
			var setting = $.extend({
				'location':'top',
				'bg-color':'blue'
			},opt||{}) ;
			
			// 返回this,维持链式操作
			return this.each(function(){
				
			}) ;
		}
		
	})(jQuery) ;
	
	// 使用(usage)
	$('div').toolTip({'location':'left'}) ;



这里有个疑问,在jQueryeasyui中默认的写法是采取一个名称空间的如

$.fn.panel.defaults = {
	title: null,
	iconCls: null,
	width: 'auto',
	height: 'auto',
	left: null
	//....
};

的写法,然后使用data进行了值的初始,问题就是在这里 easyui中是怎样通过data()方法来判断是设定初始值还是进行方法的调用的,巧妙的完成默认值的复写或方法的调用?虽然看了源码,但小弟天资愚笨没看太懂,希望知道的大牛可以指点一二。



六、Plugin方法的最佳实践是在没有特殊情况下,一个插件在$.fn上定义的一命名空间就够了,好的做法是将这些方法放入一个对象字面量中,使用方法名称去调用这些方法,


$.fn.toolTip = function(opt, param){

    // 命名调用函数,使用一个对象直接量来进行保存
    var methods = {
        // options传入的参数
        init: function(options){
            // 位置jQuery函数链,在调用处可以继续以链式的方式进行操作
            return this.each(function(){
                // methods.reposition指定的是一个回调函数
                $(window).bind('click.toolTip', methods.reposition)
            });
        },
        destroy: function(options){
            return this.each(function(){
                $(window).unbind('click.toolTip');
            });
        },
        // 事件触发后调用的函数
        reposition: function(options){
            //alert("tigger the resize event");
            $("#e").html(Math.random() + "--" + o.someVal);
        }
    };
    
    //执行指定的函数
    // this为jQuery对象
    if (typeof opt === "string") {
        if (methods[opt]) {
            return methods[opt].apply(this, Array.prototype.slice.call(arguments, 1));
        }
    }
};

在查看easyui 源码是都是将方法写在闭包里面的然后根据插入的参数进行if...else的判断调用不知道这个跟上面的那个更有有点。easyui中的写法


(function($){
	function wrapPanel(target){
		var panel = $(target).addClass('panel-body').wrap('<div class="panel"></div>').parent();
		panel.bind('_resize', function(){
			var opts = $.data(target, 'panel').options;
			if (opts.fit == true){
				setSize(target);
			}
			return false;
		});
		
		return panel;
	}
	//function ...
	if (typeof options == 'string'){
			switch(options){
			
			case 'refresh':
				return this.each(function(){
					$.data(this, 'panel').isLoaded = false;
					loadData(this);
				});
			case 'resize':
				return this.each(function(){
					setSize(this, param);
				});
			// case....
			}
		}
})(jQuery) ;


七、穿插call、apply方法的一点理解,作用都是调用一个对象的方法,一个对象替换另一个对象。Call(thisObj,[arg1],[arg2],[arg3],[argN]),Apply(thisObj,[]),所以在传入参数有区别call传入的是一个参数列表,apply传入的是一个参数数组同时可以把arguments对象传入。若没有指定thisObj参数,则global作为this指向对象,相当于java中method.invoke(obj,args)。

arguments对象封装的参数列表



八、插件事件,对于插件绑定事件的最佳实践是,对绑定的事件指定命名空间,这样做的原因是比如你对多个元素绑定了相同的一种类型的事件,在解除绑定事件的时候可能会把其他元素绑定的事件也给解除掉,造成莫名其妙的问题,意思也就是使解除事件的操作更加安全,避免对其他绑定有相同事件的元素也解除事件绑定。而具体的绑定命名事件事件是只在事件后加上.namespace如($(“div”).bind(“click.toolTip”)) ;


init: function(options){
    // 位置jQuery函数链,在调用处可以继续以链式的方式进行操作
    return this.each(function(){
        // methods.reposition指定的是一个回调函数
        $(window).bind('click.toolTip', methods.reposition)
    });
},
destroy: function(options){
    return this.each(function(){
        $(window).unbind('click.toolTip');
    });
},
// 事件触发后调用的函数
reposition: function(options){
    //alert("tigger the resize event");
    $("#e").html(Math.random() + "--" + o.someVal);
}


九、data()方法这个也是让我很纠结的一个方法,官方文档的说法是:

在日常的开发中开发者需要维护和检查插件(plugin)是否被初始化,而最佳实践就是将所有的变量放到一个对象字面量(literal)中,使用jQuery的data方法进行状态的追踪。


十、

判断一个输入值是否为数字isNaN($(this).val())或str.test(/^\d(\.\d)?$/)



十一、总结


对于一些常用的功能可以抽象成一个插件,在以后的工作中就可以直接调用
(1) 时刻要记住将你的闭包中(function($){})(jQuery)
(2) 在插件的直接范围中即上一条的闭包中不用使用$(this)来包裹this关键字,因为this关键字指向的就是jQuery对象
(3) 在插件的方法中要时刻返回this关键字,这样的做的目的是维持jQuery的链式操作
(4) 为了避免在调用插件方法是要传入所有的插件可以在插件中设置默认值,在方法调用处指定值对默认值进行复写