每一个可以努力的日子,都是一份厚礼。
写一个 jQuery 插件
工作中发现,我的很多时间都在为前端页面交互编写 JavaScript 代码。相比较而言,由于有 MVC 框架的各种约定,后端的 PHP 代码写起来显得比 JavaScript 要优雅很多。虽然也有 jQuery 这样的利器(得益于良好的跨浏览器兼容特性和简洁的使用接口,jQuery 几乎已经成了 JavaScript 的代名词),但我用到的只是它的选择器,以及别人写的一些插件而已。我深知要透彻地理解一门语言,就应该去用它做一个小项目,带着目标学习需用到的东西才能有的放矢。正好遇到一个交互需求找不到已有解决方案,于是摒弃以前那样简单粗暴的写 js 代码,我决定自己实现一个 jQuery 插件来搞定它。
JavaScript 是一门混乱的语言,好的特性和坏的特性混杂在一起。而不同浏览器对标准的解析不一致,使得这门语言更加混乱,在这种情况下遵循最佳实践有诸多好处,至少不会掉入坑里。所以就有了《JavaScript: The Good Parts》这类书专门教最佳实践。可惜读完后再去看别人的 js 代码,会发觉几乎没有谁做得很标准。
一、jQuery 插件的类别
在 jQuery 中要使用一个插件,一般有两种形式:
- 类级别。例如
$.myPlugin()
- 对象级别。例如
$('#node').myPlugin()
类级别插件可以理解为拓展 jQuery 类,即给 jQuery 添加新的全局函数,典型的例子就是 $.ajax()
这个函数。jQuery 的全局函数就是属于 jQuery 命名空间的函数。另一种是对象级别的插件,即作用于指定的 jQuery 对象,典型的例子 $('.msg').show()
。
二、类级别 jQuery 插件的开发
1. 原始写法
一般在项目中我们会引入一个 js 文件,里面存放了所有的 js 代码。
<script type="text/javascript" src="http://www.lovelucy.info/all.js"></script> |
只要写一些函数简单地放在文件里,就算是一个模块,直接调用就行了。
function m1(){ //... } function m2(){ //... } |
这种方法缺陷很明显,就是污染了全局空间,无法保证不与其他模块发生变量名冲突,而且方法成员之间看不出直接关系。
2. 扩展写法
使用 jQuery.extend(object) 来扩展 jQuery 类本身,可以理解为 jQuery 添加静态方法。
$.extend({ addMethod : function(a, b){return a + b;} }); // $.addMethod(1, 2); //return 3 |
3. 简单写法
给 jQuery 添加一个全局函数,只需如下定义
jQuery.foo = function() { alert('test'); // other code... }; |
这样就能直接用了:jQuery.foo()
或者 $.foo()
。
4. 使用命名空间
虽然上面的 2 种写法相对于原始写法要干净很多,减少了在全局空间冲突的概率,但是在 jQuery 命名空间中,仍然不可避免某些函数或变量名可能和其他 jQuery 插件冲突。因此我们习惯再封装一层,将一些方法封装到另一个自定义的命名空间。
jQuery.myPlugin = { foo:function() { alert('test'); }, bar:function(param) { alert('test "' + param + '".'); } }; // $.myPlugin.foo(); // $.myPlugin.bar('hello'); |
采用命名空间的函数仍然是全局函数,使用独立的插件名我们可以避免命名空间内函数的冲突。
三、对象级别 jQuery 插件的开发
大部分 jQuery 插件都是对象级别的,开发一个对象级别插件会遇到闭包这个概念,简单起见先只看闭包的表现形式
(function($){ // your codes })(jQuery); |
1. 扩展写法
给 jQuery 对象添加方法,就是对 jQuery.prototype 进行扩展,为 jQuery 类添加成员方法,需要用到 jQuery.fn.extend(object);
。下面是一个例子:
$.fn.extend({ getInputText:function(){ $(this).click(function(){ alert($(this).val()); }); } }); //$("#username").getInputText(); |
2. 通用写法
对象级别的插件也可以这样定义 $.fn.myPlugin = function(){}
,同样的,和类级别相比多了一个 fn。
一个通用的对象级别插件框架——
(function($){ $.fn.myPluginName = function(options){ var defaults = {} //各种属性和参数 var options = $.extend(defaults, options); this.each(function(){ //插件的实现代码 }); }; })(jQuery); |
$.extend(defaults, options)
通过合并 defaults 和 options 来扩展默认参数,实现插件接受外部 options 参数的功能。于是我们就可以见到这样的用法:
$('#myDiv').hilight({ foreground: 'blue' }); |
3. 改进的通用写法
上面代码的一种改进是暴露插件的默认设置。这可以让插件的使用者更容易用较少的代码覆盖和修改插件。
(function($){ $.fn.myPluginName = function(options){ var options = $.extend({}, $.fn.myPluginName.defaults, options); this.each(function(){ //插件的实现代码 }); }; // 暴露的默认参数 $.fn.myPluginName.defaults = {}; })(jQuery); |
于是我们就可以见到这样使用的
$.fn.hilight.defaults.foreground = 'blue'; $('#myDiv').hilight(); |
覆盖默认的配置就只需要调用一次,而不必在每次调用插件时都传递参数。是否需要传递参数,在不同的场景下可以灵活处理,两者的使用可以结合起来。
更高级的插件写法还包括暴露一些函数给使用者,让他们可以覆盖。另一方面,也可以保持私有函数的私有性。具体的代码这里就不多赘述了。学习的最好方式就是阅读别人的插件代码。
四、总结
我之前遇到的需求是要让用户在表单的一个 input 字段中输入 json 格式的文本。但是用户纯手工输入很难写出标准的 json string,借助我写的插件,可以将 input 转换为一个 key-value 的表格,用户就很容易输入了。在表单提交时 key-value 将自动 encode 为 json 回填到原来的字段中进行提交。
插件开源在 GitHub: jQuery Key-Value Json Input Plugin
这是我的第一个 jQuery 插件,当然肯定会有更好的设计方案,只是在实现这个插件的过程中,我慢慢对 jQuery 的闭包,链式操作,函数式的设计思想(匿名函数)等有了初步的概念。总结是一个反刍的过程,写完本文似乎理解又加深了一些。
之前我还和同事讨论过,Engineering 就是如此,和 Science 最大的不同就是需要总结、积累。Science 一开始就会给我们灌输概念、公式、定理,世界就是这个样子,然后用这些公式、定理去解释现象,计算结果。而 Engineering 则恰恰相反,是在长期的实践中发现规律和 Best Practice。如果一开始就搬理论,学生往往不知所云,反而是在累积了很多年工作经验以后顿悟:“是的,就是这样,就是这样”,我在大学本科上《软件工程》课时这种感觉尤为强烈。总结是必要的,这也是写 blog 的意义所在。
保持强烈的求知欲,做一个乐于学习和自我提升的人。
这篇文章由lovelucy于2013-01-06 12:56发表在前端开发。你可以订阅RSS 2.0 也可以发表评论或引用到你的网站。除特殊说明外文章均为本人原创,并遵从署名-非商业性使用-相同方式共享创作协议,转载或使用请注明作者和来源,尊重知识分享。 |
批评不自由
则赞美无意义
Mozilla Firefox 36.0 Windows 8 大约9年前
对这块很有帮助参考,努力学习wp jQuery的插件
Google Chrome 23.0.1271.97 Windows 7 大约11年前
补充一点,保险起见,最好在写好的插件js文件开头加一个分号。。。
Google Chrome 23.0.1271.97 Windows 7 大约11年前
😳 看到你在 segmentfault 上的回答,学习了
Google Chrome 23.0.1271.97 Windows 7 大约11年前
访问我这儿也要挂美国代理么?还是你 VPN 常年都挂着。。?
Google Chrome 23.0.1271.97 Windows 7 大约11年前
貌似不用了,不过看了下F12里面的network,跟google有关的东西挂了:show_ads.js, google_plus的fastbutton;还有一个http://tajs.qq.com的