jQuery插件编写及链式编程模型小结

时间:2022-06-03 00:03:22

  JQuery极大的提高了我们编写JavaScript的效率,让我们可以愉快的编写代码,做出各种特效。大多数情况下,我们都是使用别人开发的JQuery插件,今天我们就来看看如何把我们常用的功能做出JQuery插件,然后像使用jQuery那样来操作DOM.

 一、jQuery插件开发快速上手

1、jQuery插件模板

关于jQuery插件的编写,我们可以通过为jQuery.fn增加一个新的函数来编写jQuery插件。属性的名字就是你的插件的名字,其模板如下:

(function($){
$.fn.myJQPlugin = function(){
//TODO:add your code
}
})(jQuery);

其中myJQPlugin 就是你插件的名字,Function里面的代码就是你插件的功能实现代码。

2、与DOM交互

给插件起好名字后,下面就可以编写我们的代码了,但是编写之前,必须要说一说上下文。在插件内部的范围中,this关键字指向的是jQuery对象。人们很容易误解这一点,因为在正常使用jQuery的时候,this通常指向的是一个DOM元素。不了解这一点,会经常使用$又包装了一次。

(function( $ ){
$.fn.myJQPlugin = function() {
// 没有必要使用$(this)
// $(this) 跟 $($('#element'))是一样的
this.html("Hello,world");
this.click(function(){
this.hide(); //注意这里的this不再指向jQuery元素,这里的this指向当前这个function对象
});
};
})( jQuery );

这里又要提到this的指向问题,比如我们想实现一个功能,点击DOM元素,隐藏当前元素,虽然说this指向的是jQuery对象,但是在click中,this的指向发生了变化,指向了它所在的function.所以我们在click方法里面访问当前元素,就要通过变量了,实现方法如下:

(function( $ ){
$.fn.myJQPlugin = function() {
// 没有必要使用$(this)
// $(this) 跟 $($('#element'))是一样的
this.html("Hello,world");
var $this = $(this);
this.click(function(){
$this.hide(); //注意这里的this不再指向jQuery元素,这里的this指向当前这个function对象
});
};
})( jQuery );

我们声明$this变量来保存this对象,这样我们需要使用当前元素时,直接使用$this就可以了。下面我们通过一个完整的实例来测试一下:

其中html页面的代码如下:

<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="jquery.js" type="text/javascript"></script>
<script src="1.js" type="text/javascript"></script>
</head>
<body>
<div id="container" style="width:800px;height:200px; border:2px #000 solid;padding:20px;font-size:20px;">Hello,world</div>
</body>
<script type="text/javascript">
$('#container').myJQPlugin();
</script>
</html>

jQuery插件的js代码如下:

(function($){
$.fn.myJQPlugin = function(){
this.css("color","red"); //字体颜色为红色
this.hide();
this.slideDown(200); //先隐藏,然后通过slideDown显示出来
var $this = $(this);
this.click(function(){
$this.html("Thanks,good bye!"); //显示信息,然后淡出
$this.fadeOut(2000);
});
}
})(jQuery);

这个插件的功能就是首先让元素下拉显示,然后点击元素时显示再见信息,然后经过2s后淡出。这里大家可以试试在click事件里面使用this会怎么样。

 二、jQuery的链式操作

1、为什么要用链式操作?

实际上链式操作仅仅是通过对象上的方法最后加上return this. 把对象再返回回来,对象当然可以继续调用方法啦,所以就可以链式操作了。那么,简单实现一个:

//定义一个JS类
function Demo() { }
//扩展它的prototype
Demo.prototype ={
setName:function (name) {
this.name = name;
return this;
},
getName:function () {
return this.name;
},
setAge:function (age) {
this.age = age;
return this;
}
}; ////工厂函数
function D() {
return new Demo();
}
//去实现可链式的调用
D().setName("CJ").setAge(18).setName();

一般的解释:

节省代码量,代码看起来更优雅。例如如果没有链式,那么你可能需要这样写代码:

document.getElementById("ele").dosomething();
document.getElementById("ele").dootherthing();

这个代码中调用了两次document.getElementById来获取DOM树的元素,这样消耗比较大,而且要写两行,而链式只要写一行,节省了代码……

但我们也可以用缓存元素啊。比如:

var ele = document.getElementById("ele");
ele.dosomething();
ele.dootherthing();

而且两行并没有比一行多多少代码,甚至相应的封装反而使得代码更多了。
最糟糕的是所有对象的方法返回的都是对象本身,也就是说没有返回值,这不一定在任何环境下都适合。

2、那么到底为什么要用链式操作呢?

为了更好的异步体验Javascript是无阻塞语言,所以他不是没阻塞,而是不能阻塞,所以他需要通过事件来驱动,异步来完成一些本需要阻塞进程的操作。

但是异步编程是一种令人疯狂的东西……运行时候是分离的倒不要紧,但是编写代码时候也是分离的就……

常见的异步编程模型有哪些呢?

(1)回调函数 :

所谓的回调函数,意指先在系统的某个地方对函数进行注册,让系统知道这个函数的存在,然后在以后,当某个事件发生时,再调用这个函数对事件进行响应。

function fun(num, callback){
if(num < 0) {
alert("分数不能为负,输入错误!");
}else if(num == 0){
alert("该学生可能未参加考试!");
}else {
alert("调用高层函数处理!");
setTimeout(function(){callback();}, 1000);
}
}

这里callback则是回调函数。可以发现只有当num为非负数时候callback才会调用。
但是问题,如果我们不看函数内部,我们并不知道callback会几时调用,在什么情况下调用,代码间产生了一定耦合,流程上也会产生一定的混乱。

虽然回调函数是一种简单而易于部署的实现异步的方法,但从编程体验来说它却不够好。

(2)链式异步 :

个人觉得链式操作最值得称赞的还是其解决了异步编程模型的执行流程不清晰的问题。jQuery中$(document).ready就非常好的阐释了这一理念。DOMContentLoaded是一个事件,在DOM并未加载前,jQuery的大部分操作都不会奏效,但jQuery的设计者并没有把他当成事件一样来处理,而是转成一种“选其对象,对其操作”的思路。$选择了document对象,ready是其方法进行操作。这样子流程问题就非常清晰了,在链条越后位置的方法就越后执行。

(function(){
var isReady=false; //判断onDOMReady方法是否已经被执行过
var readyList= [];//把需要执行的方法先暂存在这个数组里
var timer;//定时器句柄
ready=function(fn) {
if (isReady )
fn.call( document);
else
readyList.push( function() { return fn.call(this);});
return this;
}
var onDOMReady=function(){
for(var i=0;i<readyList.length;i++){
readyList[i].apply(document);
}
readyList = null;
}
var bindReady = function(evt){
if(isReady) return;
isReady=true;
onDOMReady.call(window);
if(document.removeEventListener){
document.removeEventListener("DOMContentLoaded", bindReady, false);
} else if(document.attachEvent){
document.detachEvent("onreadystatechange", bindReady);
if(window == window.top){
clearInterval(timer);
timer = null;
}
}
};
if(document.addEventListener){
document.addEventListener("DOMContentLoaded", bindReady, false);
}else if(document.attachEvent){
document.attachEvent("onreadystatechange", function(){
if((/loaded|complete/).test(document.readyState))
bindReady();
});
if(window == window.top){
timer = setInterval(function(){
try{
isReady||document.documentElement.doScroll('left');//在IE下用能否执行doScroll判断dom是否加载完毕
}
catch(e){
return;
}
bindReady();
},5);
}
}
})();

上面的代码不能用$(document).ready,而应该是window.ready。

(3)Promise :

CommonJS中的异步编程模型也延续了这一想法,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。

所以我们可以这样写:
f1().then(f2).then(f3);
这种方法我们无需太过关注实现,也不太需要理解异步,只要懂得通过函数选对象,通过then进行操作,就能进行异步编程。

 三、维护链式开发的特性

  在jQuery中,一个插件紧紧是修改收集到的元素,然后返回这个元素让链条上的下一个使用。这是jQuery设计的精美之处,也是jQuery如此流行的原因之一。为了保证可链式,你必须返回this。如下代码:

(function( $ ){
$.fn.resize = function( type, value ) {
return this.each(function() {
var $this = $(this);
if (!type || type == 'width' ) {
$this.width(value);
}
if ( !type || type == 'height' ) {
$this.height(value);
}
});
};
})(jQuery);

调用方法如下:

 $('div').resize('width', '600').css('color','blue'); 
 四、jQuery插件编写总结

编写jQuery插件可以充分利用库,将公用的函数抽象出来,“循环利用”。以下是简短的总结:

  • 使用(function($){//plugin})(jQuery);来包装你的插件
  • 不要在插件的初始范围中重复包裹
  • 除非你返回原始值,否则返回this指针来保证可链式
  • 不要用一串参数,而是使用一个对象,并且设置默认值
  • 一个插件,不要为jQuery.fn附上多个函数
  • 为你的函数,事件,数据附着到某个命名空间
 五、参考资料

jQuery插件开发      http://www.cnblogs.com/playerlife/archive/2012/05/11/2495269.html

jQuery链式操作如何实现以及为什么要用链式操作    http://www.jb51.net/article/33342.htm

作者:雲霏霏

QQ交流群:243633526

博客地址:http://www.cnblogs.com/yunfeifei/

声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。

如果大家感觉我的博文对大家有帮助,请推荐支持一把,给我写作的动力。