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函数
May 27 Javascript
jquery中获取select选中值的代码
Jun 27 Javascript
JavaScript基础语法让人疑惑的地方小结
May 23 Javascript
jquery设置text的值示例(设置文本框 DIV 表单值)
Jan 06 Javascript
教你如何使用firebug调试功能了解javascript闭包和this
Mar 04 Javascript
flash+jQuery实现可关闭及重复播放的压顶广告
Apr 15 Javascript
小心!AngularJS结合RequireJS做文件合并压缩的那些坑
Jan 09 Javascript
Vue2.0+Vux搭建一个完整的移动webApp项目的示例
Mar 19 Javascript
30分钟用Node.js构建一个API服务器的步骤详解
May 24 Javascript
vue 获取视频时长的实例代码
Aug 20 Javascript
js中复选框的取值及赋值示例详解
Oct 18 Javascript
javascript实现倒计时关闭广告
Feb 09 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
这部好评如潮的动漫 知名梗频出 但是画风劝退很多人
2020/03/08 日漫
乱谈我对耳机、音箱的感受
2021/03/02 无线电
基于HBase Thrift接口的一些使用问题及相关注意事项的详解
2013/06/03 PHP
php去掉URL网址中带有PHPSESSID的配置方法
2014/07/08 PHP
基于PHP实现用户在线状态检测
2020/11/10 PHP
JavaScript判断一个URL链接是否有效的实现方法
2011/10/08 Javascript
node.js chat程序如何实现Ajax long-polling长链接刷新模式
2012/03/13 Javascript
js控制CSS样式属性语法对照表
2012/12/11 Javascript
禁止IE用右键的JS代码
2013/12/30 Javascript
js 获取页面高度和宽度兼容 ie firefox chrome等
2014/05/14 Javascript
JavaScript中textRange对象使用方法小结
2015/03/24 Javascript
json传值以及ajax接收详解
2016/05/24 Javascript
微信小程序 数组中的push与concat的区别
2017/01/05 Javascript
微信小程序商品详情页规格属性选择示例代码
2017/10/30 Javascript
mpvue 如何使用腾讯视频插件的方法
2018/07/16 Javascript
python实现迭代法求方程组的根过程解析
2019/11/25 Javascript
vue-cli打包后本地运行dist文件中的index.html操作
2020/08/12 Javascript
Python写的英文字符大小写转换代码示例
2015/03/06 Python
Python中数组,列表:冒号的灵活用法介绍(np数组,列表倒序)
2018/04/18 Python
Python实现的求解最小公倍数算法示例
2018/05/03 Python
pandas.DataFrame删除/选取含有特定数值的行或列实例
2018/11/07 Python
Django 自动生成api接口文档教程
2019/11/19 Python
python获取依赖包和安装依赖包教程
2020/02/13 Python
Python存储读取HDF5文件代码解析
2020/11/25 Python
Python爬取梨视频的示例
2021/01/29 Python
德国药房apodiscounter中文官网:德国排名前三的网上药店
2019/06/03 全球购物
Diptyque英国官方网站:源自法国的知名香氛品牌
2019/08/28 全球购物
美国木工工具和用品商店:Woodcraft
2019/10/30 全球购物
在校生党员自我评价
2013/09/25 职场文书
毕业生就业自荐书
2013/12/15 职场文书
目标责任书范文
2014/04/14 职场文书
幼儿园户外活动总结
2014/07/04 职场文书
中华魂放飞梦想演讲稿
2014/08/26 职场文书
幼儿园教师个人工作总结2015
2015/05/12 职场文书
早恋主题班会
2015/08/14 职场文书
《将心比心》教学反思
2016/02/23 职场文书