jQuery本身功能强大,又提供了超强的可扩展性,便于人们为其开发各种插件,本文便讲解如何开发jQuery自定义插件。有幸遇到下面这篇前端大牛的神作,遂本文仅参照此文略作修改,再次感谢这位前端大牛的神作。
文档来源:http://www.cnblogs.com/Wayou/p/jquery_plugin_tutorial.html
本文jQuery插件所操作的html代码如下:
<nav>
<ul id="nav">
<li><a href="http://www.baidu.com" title="首页" target="_blank">首页</a></li>
<li><a href="http://www.sina.com" title="新闻" target="_blank">新闻</a></li>
<li><a href="http://www.qq.com" title="评论" target="_blank">评论</a></li>
</ul>
</nav>
本文将要用到的插件调用方式如下:
<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="jquery.change.min.js"></script>
<script type="text/javascript">
$(function(){
$("#nav").find('li').change({
"border":"1px solid blue",
"fontSize":"16px"
})
})
</script>
在如何开发jQuery自定义插件一 中,讲到第二种开发插件方式的第5步《保护默认参数》,本文继续后续章节
3.6 面向对象的插件开发
再进一步,面向对象的插件开发
到此,一个简单的插件已经开发完成
但如要编写一个复杂的插件,代码量会很大,如何组织代码就成了一个需要面临的问题,没有一个好的方式来组织这些代码,整体感觉会杂乱无章,同时也不好维护,所以将插件的所有方法属性包装到一个对象上,用面向对象的思维来进行开发,无疑会使工作轻松很多。
如果没有面向对象的思维,就会是需要一个方法的时候就去定义一个function,当需要另外一个方法的时候,再去随便定义一个function,同样,需要一个变量的时候,毫无规则地定义一些散落在代码各处的变量。这样不方便维护,代码结构也不够清晰
所以正确的方法是:将需要的重要变量定义到对象的属性上,函数变成对象的方法,当我们需要的时候通过对象来获取,这样做的好处:
一来方便管理,
二来不会影响外部命名空间,因为所有这些变量名还有方法名都是在对象内部。
接着上面的例子,我们可以把这个插件抽象成一个美化页面的对象,它的功能是设置颜色、字体,还可以加入其他功能比如设置下划线等。对于这个例子抽象成对象有点小题大做,这里只是借这个例子说明如果开发面向对象的插件。
//新建一个对象命名为Beautifier,然后在插件里使用这个对象来编码。
//定义Beautifier的构造函数
var Beautifier = function(ele,opt){
//ele接收插件中的this实参,为jQuery选中的元素集合
//本Beautifier对象内部的this是DOM对象,不是jQuery类型
this.$element = ele,
//默认参数
this.defaults = {
'border':'1px solid red',
'fontSize':'14px',
"marginBottom":"5px",
"color":"#f00"
}
//合并参数
this.options = $.extend({},this.defaults,opt);
//以后在此处添加新的变量即可,格式为:
//this.变量名 = 变量值
}
//定义Beautifier的方法
Beautifier.prototype = {
beautify:function(){
return this.$element.css({
'border':this.options.border,
'fontSize':this.options.fontSize,
"marginBottom":this.options.marginBottom,
"color":this.options.color
});
}
//以后在此处添加新的方法即可,格式为:
//,方法名:function(){
// return this.$element.jQuery方法()
//}
}
//在插件中使用Beautifier对象
$.fn.change = function(options){
//创建Beautifier的实体
//this为jQuery选中的元素集合
var beautifier = new Beautifier(this,options);
//调用其beautify方法
return beautifier.beautify();
//以后在此处调用新的方法即可,格式为:
//return beautifier.方法名()
}
通过上面这样一改造,代码变得更面向对象了,也更好维护和理解,以后要加新功能新方法,只需向对象添加新变量及方法即可,然后在插件里实例化后即可调用新添加的东西。
插件的调用还是一样的,对代码的改动并不影响插件其他地方,只是将代码的组织结构改动了而以。
到这里,就可以更好地编写复杂的插件同时很好地组织代码了
3.7 创建独立的命名空间,即用自调用匿名函数包裹你的代码
再进一步,创建独立的命名空间,即用自调用匿名函数包裹你的代码
不仅仅是jQuery插件的开发,我们在写任何JS代码时都应该注意的一点是不要污染全局命名空间。因为随着你代码的增多,如果有意无意在全局范围内定义一些变量的话,最后很难维护,也容易跟别人写的代码有冲突。
比如在代码中向全局window对象添加了一个变量status用于存放状态,
同时页面中引用了另一个别人写的库,他也向全局添加了这样一个同名变量,最后的结果肯定不是你想要的。所以不到万不得已,一般我们不会将变量定义成全局的。但有时全局变量又是非加不可的
JavaScript中无法用花括号方便地创建作用域,但函数却可以形成一个作用域,域内的代码是无法被外界访问的。如果我们将自己的代码放入一个函数中,那么就不会污染全局命名空间,同时不会和别的代码冲突。
如上面定义了一个Beautifier全局变量,它会被附到全局的window对象上,为了防止这种事情发生,你或许会说,把所有代码放到jQuery的插件定义代码里面去啊,也就是放到$.fn.myPlugin里面。
这样做倒也是种选择。但会让我们实际跟插件定义有关的代码变得臃肿,
而在$.fn.myPlugin里面我们其实应该更专注于插件的调用,以及如何与jQuery互动。
好的做法是始终用自调用匿名函数包裹你的代码,这样就可以完全放心,安全地将它用于任何地方了,绝对没有冲突
自调用匿名函数的结构:
(function(形参){
//代码部分
})(实参)
所以保持上面例子的代码不变,将所有代码用自调用匿名函数包裹即可。
这样做还有一个好处就是,自调用匿名函数里面的代码会在第一时间执行,页面准备好过后,上面的代码就将插件准备好了,以方便在后面的代码中使用插件。
到目前为止插件的代码接近完美了。但还是要再考虑到其他一些因素
3.8 自调用匿名函数前添加分号
如果将自调用匿名函数这段代码放到页面最后,而其前面是别人写的代码没有用分号结尾,那结果将是不可预测的。这是因为用来充当自调用匿名函数的第一对括号与上面别人定义的函数相连(因为中间没有分号)。这将导致我们的代码无法正常解析,所以报错。
好的做法是在自调用匿名函数前加一个分号,这在任何时候都是一个好的习惯。
代码如下:
;(function(){//注意这里的分号必须加
//所有插件的代码
})()
3.9 将系统变量以变量形式传递到插件内部
如果我们插件代码之前的代码里有将window, undefined等这些系统变量或者关键字修改掉了,正好我们又在自己的插件代码里面进行了使用,那结果也是不可预测的
好的做法是将系统变量以参数形式传递到插件内部
当这样做之后,window等系统变量在插件内部就有了一个局部的引用,可以提高访问速度,性能也可能会有提升
最终,我们得到一个非常安全、结构良好的代码框架如下:
;(function($,window,document,undefined){//注意这里的分号必须加
//插件的全部代码
})(jQuery,window,document);
至于其中的undefined,稍微有意思一点,为了得到没有被修改的undefined,我们并没有传递这个参数,但却在接收时接收了它,因为实际并没有传,所以‘undefined’那个位置接收到的就是真实的’undefined’了。
所以最终的插件代码如下:
;(function($,window,document,undefined){//注意这里的分号必须加
//插件的全部代码
//定义Beautifier的构造函数
var Beautifier = function(ele,opt){
//ele接收插件中的this实参,为jQuery选中的元素集合
//本Beautifier对象内部的this是DOM对象,不是jQuery类型
this.$element = ele,
//默认参数
this.defaults = {
'border':'1px solid red',
'fontSize':'14px',
"marginBottom":"5px",
"color":"#f00"
}
//合并参数
this.options = $.extend({},this.defaults,opt);
//以后在此处添加新的变量即可,格式为:
//this.变量名 = 变量值
}
//定义Beautifier的方法
Beautifier.prototype = {
beautify:function(){
// 此处的this是调用该方法的Beautifier对象的实例
// 因this在不同的方法里,指向的对象不同,所以此处保存this引用到self变量中
self = this;
return this.$element.each(function(){
//each方法里的的this是this.$element集合里的每一个具体元素的DOM类型
//所以需要将this进行jQuery包装(即$(this))后,才能调用jQuery的方法
$(this).css({
'border':self.options.border,
'fontSize':self.options.fontSize,
"marginBottom":self.options.marginBottom,
"color":self.options.color
});
$(this).append(' ' + $(this).find("a").attr('href'));
})
}
//以后在此处添加新的方法即可,格式为:
//,方法名:function(){
// return this.$element.jQuery方法()
//}
}
//在插件中使用Beautifier对象
$.fn.change = function(options){
//创建Beautifier的实体
//this为jQuery选中的元素集合
var beautifier = new Beautifier(this,options);
//调用其beautify方法
return beautifier.beautify();
//以后在此处调用新的方法即可,格式为:
//return beautifier.方法名()
}
})(jQuery,window,document);
一个命名空间安全、结构良好、面向对象、组织有序的插件编写完成。
附 关于变量定义及命名
现在谈谈关于变量及方法等的命名,没有硬性规定,但为了规范,遵循一些约定还是很有必要的。
变量定义:好的做法是把将要使用的所有变量名用一个var关键字一起定义在代码开头,变量名间用逗号隔开。
原因有二:
一是便于理解,知道下面的代码会用到哪些变量,同时代码显得整洁且有规律,也方便管理,变量定义与逻辑代码分开;
二是因为JavaScript中所有变量及函数名会自动提升,也称之为JavaScript的Hoist特性,即使你将变量的定义穿插在逻辑代码中,在代码解析运行期间,这些变量的声明还是被提升到了当前作用域最顶端的,
所以我们将变量定义在一个作用域的开头是更符合逻辑的一种做法。当然,再次说明这只是一种约定,不是必需的。
变量及函数命名: 一般使用驼峰命名法(CamelCase),即首个单词的首字母小写,后面单词首字母大写
比如resultArray,requestAnimationFrame。
常量命名: 所有字母采用大写,多个单词用下划线隔开,
比如WIDTH=100,BRUSH_COLOR=’#00ff00’。
当变量是jQuery类型时,建议以$开头,因为这可以很方便地将它与普通变量区别开来
这样一看到以$开头就知道它是jQuery类型可以直接在其身上调用jQuery相关的方法,比如var $element=$(‘a’);
之后就可以在后面的代码中很方便地使用$element.css()、$element.first()等等,并且与普通变量容易区分开来。
引号的使用:一般HTML代码里面使用双引号,而在JavaScript中多用单引号,
如下面代码所示:
var name = 'Wayou';
document.getElementById(‘example’).innerHTML = '< a href="http: wayouliu.duapp.com/">'+name+'</a>';
//href=".." HTML中保持双引号,JavaScript中保持单引号
这样的好处:
一方面,HTML代码中本来就使用的是双引号,
另一方面,在JavaScript中引号中还需要引号的时候,要求我们单双引号间隔着写才是合法的语句,除非你使用转意符那也是可以的。
再者,坚持这样的统一可以保持代码风格的一致,不会出现这里的字符串用双引号包着,另外的地方在用单引号。
附 代码混淆与压缩
进行完上面的步骤,已经小有所成了。或许你很早就注意到了,你下载的插件里面,一般都会提供一个压缩的版本一般在文件名里带个’min’字样。也就是minified的意思,压缩浓缩后的版本。并且平时我们使用的jQuery也是官网提供的压缩版本,jquery.min.js。
这里的压缩不是指代码进行功能上的压缩,而是通过将代码里面的变量名,方法函数名等等用更短的名称来替换,并且删除注释(如果有的话)删除代码间的空白及换行所得到的浓缩版本。同时由于代码里面的各种名称都已经被替代,别人无法阅读和分清其逻辑,也起到了混淆代码的作用。
压缩的好处
源码经过混淆压缩后,体积大大减小,使代码变得轻量级,同时加快了下载速度,页面加载变快。比如正常jQuery v1.11.0的源码是276kb,而压缩后的版本仅94.1kb!体积减小一半还多。这个体积的减小对于文件下载速度的提升不可小觑。
经过压缩混淆后,代码还能阅读嘛?当然不能,所以顺带还起到了代码保护的作用。当然只是针对你编写了一些比较酷的代码又不想别人抄袭的情况。对于jQuery社区,这里本身就是开源的世界,同时JavaScript这东西其实也没什么实质性方法可以防止别人查看阅读你的代码,毕竟有混淆就有反混淆工具,这里代码压缩更多的还是上面提到的压缩文件的作用,同时一定程度上防止别人抄袭。
工具
所使用的工具推崇的是Google开发的Closure Compiler。该工具需要Java环境的支持,所以使用前你可能需要先在机子上装JRE, 然后再获取Closure进行使用。
同时也有很朋在线的代码混淆压缩工具,用起来也很方便。这些工具都是一搜一大把的。