jQuery插件面向对象开发
为什么要有面向对象的思维,因为如果不这样,你可能需要一个方法的时候就去定义一个function,当需要另外一个方法的时候,再去随便定义一个function,同样,需要一个变量的时候,毫无规则地定义一些散落在代码各处的变量,不方便维护,也不够清晰。当然,这些问题在代码规模较小时是体现不出来的。
如果将需要的重要变量定义到对象的属性上,函数变成对象的方法,当我们需要的时候通过对象来获取,一来方便管理,二来不会影响外部命名空间,因为所有这些变量名还有方法名都是在对象内部。
接着上面的例子,我们可以把这个插件抽象成一个美化页面的对象,因为他的功能是设置颜色啊字体啊什么的,当然我们还可以加入其他功能比如设置下划线啊什么的,当然对于这个例子抽象成对象有点小题大做,这里为了学习jQuery插件用面向对象思想开发,故演示使用。
我们新建一个Myobj对象,然后我们在插件里使用这个对象来编码,如下:
var Myobj = function (ele,options){ this .element = ele; this .defaults = { 'color' : 'red' , 'fontSize' : '12px' }; this .opts = $.extend({}, this .defaults,options); } Myobj.prototype = { myfun: function (){ this .element.css({ 'color' : this .opts.color, 'font-size' : this .opts.fontSize }); return this ; } } $.fn.myPlugin = function (options){ var myobj = new Myobj( this ,options); myobj.myfun(); return this ; }; |
这个例子的效果与让你的jQuery提升一个台阶,jQuery插件开发教程是一样的,通过我们用面向对象的思想来写jquery插件使代码更容易维护和理解,以后要加新功能新方法,只需向对象添加新变量及方法即可,然后在插件里实例化后即可调用新添加的东西。
到这里,你可以更好地编写复杂的插件同时很好地组织代码了,当我们回头去看上面的代码时,其实也还是有改进空间的,下面用命名空间改造一下代码。
关于命名空间
不仅仅是jQuery插件的开发,我们在写任何JS代码时都应该注意的一点是不要污染全局命名空间。因为随着你代码的增多,如果有意无意在全局范围内定义一些变量的话,最后很难维护,也容易跟别人写的代码有冲突。
比如你在代码中向全局window对象添加了一个变量status用于存放状态,同时页面中引用了另一个别人写的库,也向全局添加了这样一个同名变量,最后的结果肯定不是你想要的。所以不到万不得已,一般我们不会将变量定义成全局的。
一个好的做法是始终用自调用匿名函数包裹你的代码,这样就可以完全放心,安全地将它用于任何地方了,绝对没有冲突。
用自调用匿名函数包裹你的代码
我们知道JavaScript中无法用花括号方便地创建作用域,但函数却可以形成一个作用域,域内的代码是无法被外界访问的。如果我们将自己的代码放入一个函数中,那么就不会污染全局命名空间,同时不会和别的代码冲突。
如上面我们定义了一个myobj全局变量,它会被附到全局的window对象上,为了防止这种事情发生,你或许会说,把所有代码放到 jQuery的插件定义代码里面去啊,也就是放到$.fn.myPlugin里面。这样做倒也是种选择。但会让我们实际跟插件定义有关的代码变得臃肿,而在$.fn.myPlugin里面我们其实应该更专注于插件的调用,以及如何与jQuery互动。
所以保持原来的代码不变,我们将所有代码用自调用匿名函数包裹:
( function (){ //定义Myobj构造函数 var Myobj = function (ele,options){ this .element = ele; this .defaults = { 'color' : 'red' , 'fontSize' : '12px' }; this .opts = $.extend({}, this .defaults,options); } //定义myfun方法 Myobj.prototype = { myfun: function (){ this .element.css({ 'color' : this .opts.color, 'font-size' : this .opts.fontSize }); return this ; } } $.fn.myPlugin = function (options){ //在插件中使用Myobj对象 var myobj = new Myobj( this ,options); //调用其方法 myobj.myfun(); //支持链式调用 return this ; }; })(); |
这样做的好处,也就是上面所阐述的那样。另外还有一个好处就是,自调用匿名函数里面的代码会在第一时间执行,页面准备好过后,上面的代码就将插件准备好了,以方便在后面的代码中使用插件。
目前为止似乎接近完美了。如果再考虑到其他一些因素,比如我们将这段代码放到页面后,前面别人写的代码没有用分号结尾,或者前面的代码将 window, undefined等这些系统变量或者关键字修改掉了,正好我们又在自己的代码里面进行了使用,那结果也是不可预测的,这不是 我们想要的。
将系统变量以变量形式传递到插件内部:
来看下面的代码,你猜他会出现什么结果?
var foo= function (){ //别人的代码 } //注意这里没有用分号结尾 //自己的代码 ( function (){ alert( 'Hello World!' ); })(); |
本来别人的代码也正常工作,只是最后定义的那个函数没有用分号结尾而以,然后当页面中引入我们的插件时,报错了,我们的代码无法正常执行。
原因是我们用来充当自调用匿名函数的第一对括号与上面别人定义的函数相连,因为中间没有分号嘛,总之我们的代码无法正常解析了,所以报错。
所以好的做法是我们在代码开头加一个分号,这在任何时候都是一个好的习惯。
同时,将系统变量以参数形式传递到插件内部也是个不错的实践。
当我们这样做之后,window等系统变量在插件内部就有了一个局部的引用,可以提高访问速度,会有些许性能的提升
最后我们得到一个非常安全结构良好的代码:
;( function ($,window,document,undefined){ //我们的代码 })(jQuery,window,document); |
而至于这个undefined,稍微有意思一点,为了得到没有被修改的undefined,我们并没有传递这个参数,但却在接收时接收了它,因为实际并没有传,所以‘undefined’那个位置接收到的就是真实的'undefined'了。是不是有点hack的味道,值得细细体会的技术,当然不是我发明的,都是从前人的经验中学习的。
所以最后我们的插件成了这样:
( function ($, window, document, undefined){ //定义Myobj构造函数 var Myobj = function (ele,options){ this .element = ele; this .defaults = { 'color' : 'red' , 'fontSize' : '12px' }; this .opts = $.extend({}, this .defaults,options); } //定义myfun方法 Myobj.prototype = { myfun: function (){ this .element.css({ 'color' : this .opts.color, 'font-size' : this .opts.fontSize }); return this ; } } $.fn.myPlugin = function (options){ //在插件中使用Myobj对象 var myobj = new Myobj( this ,options); //调用其方法 myobj.myfun(); //支持链式调用 return this ; }; })(jQuery, window, document); |
一个安全,结构良好,组织有序的插件编写完成。
关于变量定义及命名
现在谈谈关于变量及方法等的命名,没有硬性规定,但为了规范,遵循一些约定还是很有必要的。
变量定义:好的做法是把将要使用的变量名用一个var关键字一并定义在代码开头,变量名间用逗号隔开。原因有二:
一是便于理解,知道下面的代码会用到哪些变量,同时代码显得整洁且有规律,也方便管理,变量定义与逻辑代码分开;
二是因为JavaScript中所有变量及函数名会自动提升,也称之为JavaScript的Hoist特性,即使你将变量的定义穿插在逻辑代码中,在代码解析运行期间,这些变量的声明还是被提升到了当前作用域最顶端的,所以我们将变量定义在一个作用域的开头是更符合逻辑的一种做法。当然,再次说明这只是一种约定,不是必需的。
变量及函数命名: 一般使用驼峰命名法(CamelCase),即首个单词的首字母小写,后面单词首字母大写,比如resultArray,requestAnimationFrame。对于常量,所有字母采用大写,多个单词用下划线隔开。当变量是jQuery类型时,建议以$开头,开始会不习惯,但经常用了之后会感觉很方便,因为可以很方便地将它与普通变量区别开来,一看到以$开头我们就知道它是jQuery类型可以直接在其身上调用jQuery相关的方法,比如var $element=$('a'); 之后就可以在后面的代码中很方便地使用它,并且与其他变量容易区分开来。
引号的使用:既然都扯了这些与插件主题无关的了,这里再多说一句,一般HTML代码里面使用双引号,而在JavaScript中多用单引号,一方面,HTML代码中本来就使用的是双引号,另一方面,在JavaScript中引号中还需要引号的时候,要求我们单双引号间隔着写才是合法的语句,除非你使用转意符那也是可以的。再者,坚持这样的统一可以保持代码风格的一致,不会出现这里字符串用双引号包着,另外的地方就在用单引号。