jQuery链式操作如何实现以及为什么要用链式操作


Posted in Javascript onJanuary 17, 2013

两个问题
1.jQuery的链式操作是如何实现的?
2.为什么要用链式操作?
大家认为这两个问题哪个好回答一点呢?

链式操作
原理相信百度一下一大把,实际上链式操作仅仅是通过对象上的方法最后
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();

而且两行并没有比一行多多少代码,甚至相应的封装反而使得代码更多了。
最糟糕的是所有对象的方法返回的都是对象本身,也就是说没有返回值,这不一定在任何环境下都适合。
举个例子,我们想弄一个超大整数BigInteger(意思是如果用Javascript的Number保存可能会溢出的整数),顺便扩展他的运算方法,会适合用链式操作么?

例如运算31415926535 * 4 - 271828182,如果设计成链式风格的方法可能会是这样的:

var result = (new BigInteger("31415926535")).multiply(new BigInteger("4")).subtract(new BigInteger("271828182")).val(); 
console.log("result == " + result);

这看起来似乎也很优雅,但是如果我们想要中间的结果怎么办呢?或许会写成这样:
var bigInteger = new BigInteger("31415926535"); 
var result1 = bigInteger.multiply(new BigInteger("4")).val(); 
var result2 = bigInteger.subtract(new BigInteger("271828182")).val(); 
console.log("result1 == " + result1 + ", result2 == " + result2);

这似乎一点也不优雅了,和不用链式操作没啥不同嘛!
那么如果要求是原来的BigInteger不能改变呢?好吧,链式操作似乎不能满足这个需求了。

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

但是异步编程是一种令人疯狂的东西……运行时候是分离的倒不要紧,但是编写代码时候也是分离的就……
常见的异步编程模型有哪些呢?
回调函数
所谓的回调函数,意指先在系统的某个地方对函数进行注册,让系统知道这个函数的存在,然后在以后,当某个事件发生时,再调用这个函数对事件进行响应。

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

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

虽然回调函数是一种简单而易于部署的实现异步的方法,但从编程体验来说它却不够好。
事件监听
也就是采用事件驱动,执行顺序取决于事件顺序。

function EventTarget(){ 
this.handlers = {}; 
} 
EventTarget.prototype = { 
constructor: EventTarget, 
addHandler: function(type, handler){ 
this.handlers[type] = []; 
}, 
fire: function(){ 
if(!event.target){ 
event.target = this; 
} 
if(this.handlers[event.type instanceof Array]){ 
var handlers = this.handlers[event.type]; 
for(var i = 0, len = handlers.length, i < len; i++){ 
handlers[i](event); 
} 
} 
}, 
removeHandler: function(type, handler){ 
if(this.handlers[type] instanceof Array){ 
var handlers = this.handlers[type]; 
for(var i = 0, le = handlers.length; i < len; i++){ 
if(handlers[i] === handler){ 
break; 
} 
} 
handlers.splice(i, 1); 
} 
} 
};

上面是《JavaScript高级程序设计》中的自定义事件实现。于是我们就可以通过addHandler来绑定事件处理函数,用fire来触发事件,用removeHandler来删除事件处理函数。

虽然通过事件解耦了,但流程顺序更加混乱了。
链式异步
个人觉得链式操作最值得称赞的还是其解决了异步编程模型的执行流程不清晰的问题。jQuery中$(document).ready就非常好的阐释了这一理念。DOMCotentLoaded是一个事件,在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。
Promise
CommonJS中的异步编程模型也延续了这一想法,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。
所以我们可以这样写
f1().then(f2).then(f3);
这种方法我们无需太过关注实现,也不太需要理解异步,只要懂得通过函数选对象,通过then进行操作,就能进行异步编程。
Javascript 相关文章推荐
JavaScript中的原型prototype属性使用详解
Jun 05 Javascript
浅谈setTimeout 与 setInterval
Jun 23 Javascript
JS绘制生成花瓣效果的方法
Aug 05 Javascript
js停止冒泡和阻止浏览器默认行为的简单方法
May 15 Javascript
js实现StringBuffer的简单实例
Sep 02 Javascript
JS实现物体带缓冲的间歇运动效果示例
Dec 22 Javascript
原生js实现手风琴功能(支持横纵向调用)
Jan 13 Javascript
js for循环倒序输出数组元素的实例
Mar 01 Javascript
Ionic3 UI组件之Gallery Modal详解
Jun 07 Javascript
knockoutjs模板实现树形结构列表
Jul 31 Javascript
微信小程序实现给嵌套template模板传递数据的方式总结
Dec 18 Javascript
微信小程序实现录制、试听、上传音频功能(带波形图)
Feb 27 Javascript
JQuery中根据属性或属性值获得元素(6种情况获取方法)
Jan 17 #Javascript
JavaScript控制Session操作方法
Jan 17 #Javascript
file模式访问网页时iframe高度自适应解决方案
Jan 16 #Javascript
jquery如何改变html标签的样式(两种实现方法)
Jan 16 #Javascript
jquery选择器的选择使用及性能介绍
Jan 16 #Javascript
jQuery旋转插件—rotate支持(ie/Firefox/SafariOpera/Chrome)
Jan 16 #Javascript
用JS提交参数创建form表单在FireFox中遇到的问题
Jan 16 #Javascript
You might like
CPU步进是什么意思?i3-9100F B0步进和U0步进区别知识科普
2020/03/17 数码科技
浅谈电磁辐射对健康的影响
2021/03/01 无线电
社区(php&amp;&amp;mysql)一
2006/10/09 PHP
PHP Reflection API详解
2015/05/12 PHP
php禁用cookie后session设置方法分析
2016/10/19 PHP
PHP实现cookie跨域session共享的方法分析
2019/08/23 PHP
数理公式,也可以这么唯美
2021/03/10 无线电
javascript 实用的文字链提示框效果
2010/06/30 Javascript
jQuery getJSON 处理json数据的代码
2010/07/26 Javascript
一次失败的jQuery优化尝试小结
2011/02/06 Javascript
使用jQuery fancybox插件打造一个实用的数据传输模态弹出窗体
2013/01/15 Javascript
使用javascript实现有效时间的控制,并显示将要过期的时间
2014/01/02 Javascript
清除div下面的所有标签的方法
2014/02/17 Javascript
javascript中setTimeout的问题解决方法
2014/05/08 Javascript
Jquery Mobile 自定义按钮图标
2015/11/18 Javascript
简单的Vue异步组件实例Demo
2017/12/27 Javascript
vue脚手架搭建项目的兼容性配置详解
2018/07/17 Javascript
ionic4+angular7+cordova上传图片功能的实例代码
2019/06/19 Javascript
微信小程序实现蒙版弹出窗功能
2019/09/17 Javascript
vue自定义正在加载动画的例子
2019/11/14 Javascript
vue 解决addRoutes多次添加路由重复的操作
2020/08/04 Javascript
可拖拽组件slider.js使用方法详解
2020/12/04 Javascript
[01:22]DOTA2神秘商店携大量周边降临完美大师赛
2017/11/07 DOTA
Python错误: SyntaxError: Non-ASCII character解决办法
2017/06/08 Python
django 创建过滤器的实例详解
2017/08/14 Python
Selenium定位元素操作示例
2018/08/10 Python
Python运维开发之psutil库的使用详解
2018/10/18 Python
python elasticsearch环境搭建详解
2019/09/02 Python
浅谈keras2 predict和fit_generator的坑
2020/06/17 Python
亚洲在线旅行门户网站:Expedia.com.hk(智游网)
2020/04/14 全球购物
护士思想汇报
2014/01/12 职场文书
竞选大队长演讲稿
2014/04/29 职场文书
村干部培训方案
2014/05/02 职场文书
2015年端午节活动方案
2015/05/05 职场文书
公司保密管理制度
2015/08/04 职场文书
运动会广播稿200字
2015/08/19 职场文书