jquery插件开发模式
jquery插件一般有三种开发方式:
- 通过$.extend()来扩展jQuery
- 通过$.fn 向jQuery添加新的方法
- 通过$.widget()应用jQuery UI的部件工厂方式创建
第一种$.extend()相对简单,一般很少能够独立开发复杂插件,第三种是一种高级的开发模式,本文也不做介绍。第二种则是一般插件开发用到的方式,本文着重讲讲第二种。
插件开发
第二种插件开发方式一般是如下定义
$.fn.pluginName = function() {
//your code here
}
插件开发,我们一般运用面向对象的思维方式
例如定义一个对象
var Haorooms= function(el, opt) {
this.$element = el,
this.defaults = {
'color': 'red',
'fontSize': '12px',
'textDecoration':'none'
},
this.options = $.extend({}, this.defaults, opt)
}
//定义haorooms的方法
haorooms.prototype = {
changecss: function() {
return this.$element.css({
'color': this.options.color,
'fontSize': this.options.fontSize,
'textDecoration': this.options.textDecoration
});
}
}
$.extend({}, this.defaults, opt)有{}主要是为了创建一个新对象,保留对象的默认值。
$.fn.myPlugin = function(options) {
//创建haorooms的实体
var haorooms= new Haorooms(this, options);
//调用其方法
return Haorooms.changecss();
}
调用这个插件直接如下就可以
$(function() {
$('a').myPlugin({
'color': '#2C9929',
'fontSize': '20px'
});
})
上述开发方法的问题
上面的开发方法存在一个严重的问题,就是定义了一个全局的Haorooms,这样对于插件的兼容等等各个方面都不好。万一别的地方用到了Haorooms,那么你的代码就悲催了!现在我们把上面的代码包装起来,用一个自调用匿名函数(有时又叫块级作用域或者私有作用域)包裹,就不会出现这个问题了!包括js插件的开发,也是一样的,我们用一个自调用匿名函数把自己写的代码包裹起来,就可以了!包裹方法如下:
(function(){
})()
用上面的这个包裹起来,就可以了。
但是还有一个问题,当我们研究大牛的代码的时候,前面经常看到有“;”,那是为了避免代码合并等不必要的错误。
例如,我们随便定义一个函数:
var haoroomsblog=function(){
}
(function(){
})()
由于haoroomsblog这个函数后面没有加分号,导致代码出错,为了避免这类情况的发生,通常这么写!
;(function(){
})()
把你的插件代码包裹在上面里面,就是一个简单的插件了。(注js插件和jquery插件都是如此)
还有一个问题
把你的插件包裹在
;(function(){
})()
基本上可以说是完美了。但是为了让你开发的插件应用更加广泛,兼容性更加好,还要考虑到用插件的人的一些特殊的做法,例如,有些朋友为了避免jquery和zeptojs冲突,将jquery的前缀“$”,修改为“jQuery”,还有些朋友将默认的document等方法修改。为了让你的插件在这些东西修了了的情况下照常运行,那么我们的做法是,把代码包裹在如下里面:
;(function($,window,document,undefined){
//我们的代码。。
})(jQuery,window,document);
就可以避免上面的一些情况了!
优秀的示例
下面,我们有了一个插件的基本层次:
(function($) {
var privateFunction = function() {
// 代码在这里运行
}
var methods = {
init: function(options) {
return this.each(function() {
var $this = $(this);
var settings = $this.data('pluginName');
if(typeof(settings) == 'undefined') {
var defaults = {
propertyName: 'value',
onSomeEvent: function() {}
}
settings = $.extend({}, defaults, options);
$this.data('pluginName', settings);
} else {
settings = $.extend({}, settings, options);
}
// 代码在这里运行
});
},
destroy: function(options) {
return $(this).each(function() {
var $this = $(this);
$this.removeData('pluginName');
});
},
val: function(options) {
var someValue = this.eq(0).html();
return someValue;
}
};
$.fn.pluginName = function() {
var method = arguments[0];
if(methods[method]) {
method = methods[method];
arguments = Array.prototype.slice.call(arguments, 1);
} else if( typeof(method) == 'object' || !method ) {
method = methods.init;
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.pluginName' );
return this;
}
return method.apply(this, arguments);
}
})(jQuery);
你可能会注意到,我所提到代码的结构和其他插件代码有很大的不同。根据你的使用和需求的不同,插件的开发方式也可能会呈现多样化。我的目的是澄清代码中的一些概念,足够让你找到适合自己的方法去理解和开发一个jQuery插件。
现在,来解剖我们的代码吧!
容器:一个即时执行函数
根本上来说,每个插件的代码是被包含在一个即时执行的函数当中,如下:
(function(arg1, arg2) {
// 代码
})(arg1, arg2);
即时执行函数,顾名思义,是一个函数。让它与众不同的是,它被包含在一对小括号里面,这让所有的代码都在匿名函数的局部作用域中运行。这并不是说DOM(全局变量)在函数内是被屏蔽的,而是外部无法访问到函数内部的公共变量和对象命名空间。这是一个很好的开始,这样你声明你的变量和对象的时候,就不用担心着变量名和已经存在的代码有冲突。
现在,因为函数内部所有的所有公共变量是无法访问的,这样要把jQuery本身作为一个内部的公共变量来使用就会成为问题。就像普通的函数一样,即时函数也根据引用传入对象参数。我们可以将jQuery对象传入函数,如下:
(function($) {
// 局部作用域中使用$来引用jQuery
})(jQuery);
我们传入了一个把公共变量“jQuery”传入了一个即时执行的函数里面,在函数局部(容器)中我们可以通过“$”来引用它。也就是说,我们把容器当做一个函数来调用,而这个函数的参数就是jQuery。因为我们引用的“jQuery”作为公共变量传入,而不是它的简写“$”,这样我们就可以兼容Prototype库。如果你不用Prototype或者其它用“$”做简写的库的话,你不这样做也不会造成什么影响,但是知道这种用法仍是一件好事。
插件:一个函数
一个jQuery插件本质上是我们塞进jQuery命名空间中一个庞大的函数,当然,我们可以很轻易地用“jQuery.pluginName=function”,来达到我们的目的,但是如果我们这样做的话我们的插件的代码是处于没有被保护的暴露状态的。“jQuery.fn”是“jQuery.prototype”的简写,意味当我们通过jQuery命名空间去获取我们的插件的时候,它仅可写(不可修改)。它事实上可以为你干点什么事呢?它让你恰当地组织自己的代码,和理解如何保护你的代码不受运行时候不需要的修改。最好的说法就是,这是一个很好的实践!
通过一个插件,我们获得一个基本的jQuery函数:
(function($) {
$.fn.pluginName = function(options) {
// 代码在此处运行
return this;
}
})(jQuery);
上面的代码中的函数可以像其他的jQuery函数那样通过“$(‘#element').pluginName()”来调用。注意,我是如何把“return this”语句加进去的;这小片的代码通过返回一个原来元素的集合(包含在this当中)的引用来产生链式调用的效果,而这些元素是被一个jQuery对象所包裹的。你也应该注意,“this”在这个特定的作用域中是一个jQuery对象,相当于“$(‘#element')”。
根据返回的对象,我们可以总结出,在上面的代码中,使用“$(‘#element').pluginName()”的效果和使用“$(‘#element')”的效果是一样的。在你的即时执行函数作用域中,没必要用“$(this)”的方式来把this包裹到一个jQuery对象中,因为this本身已经是被包装好的jQuery对象。
多个元素:理解Sizzle
jQuery使用的选择器引擎叫Sizzle,Sizzle可以为你的函数提供多元素操作(例如对所有类名相同的元素)。这是jQuery几个优秀的特性之一,但这也是你在开发插件过程中需要考虑的事情。即使你不准备为你的插件提供多元素支持,但为这做准备仍然是一个很好的实践。
这里我添加了一小段代码,它让你的插件代码为多元素集合中每个元素单独地起作用:
function($) {
// 向jQuery中被保护的“fn”命名空间中添加你的插件代码,用“pluginName”作为插件的函数名称
$.fn.pluginName = function(options

