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 相关文章推荐
用JQuery模仿淘宝的图片放大镜显示效果
Sep 15 Javascript
JavaScript包装对象使用介绍
Aug 29 Javascript
JS脚本defer的作用示例介绍
Jan 02 Javascript
我的Node.js学习之路(一)
Jul 06 Javascript
js实现点击切换TAB标签实例
Aug 21 Javascript
JS实现点击网页判断是否安装app并打开否则跳转app store
Nov 18 Javascript
原生JS轮播图插件
Feb 09 Javascript
在js中做数字字符串补0(js补零)
Mar 25 Javascript
webpack3之loader全解析
Oct 26 Javascript
vue+express 构建后台管理系统的示例代码
Jul 19 Javascript
浅谈webpack性能榨汁机(打包速度优化)
Jan 09 Javascript
Vue关于组件化开发知识点详解
May 13 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
Banner程序
2006/10/09 PHP
php基础教程 php内置函数实例教程
2012/08/21 PHP
Ajax和PHP正则表达式验证表单及验证码
2016/09/24 PHP
CodeIgniter框架基本增删改查操作示例
2017/03/23 PHP
JavaScript的目的分析
2007/01/05 Javascript
JavaScript 编程引入命名空间的方法
2007/06/29 Javascript
让你的博文自动带上缩址的实现代码,方便发到微博客上
2010/12/28 Javascript
JQuery中关于jquery.js与jquery.min.js的比较探讨
2013/05/15 Javascript
jquery动态更换设置背景图的方法
2014/03/25 Javascript
JavaScript数组方法总结分析
2016/05/06 Javascript
一个极为简单的requirejs实现方法
2016/10/20 Javascript
jQuery插件HighCharts绘制的2D堆柱状图效果示例【附demo源码下载】
2017/03/14 Javascript
vue router自动判断左右翻页转场动画效果
2017/10/10 Javascript
JavaScript模拟实现封装的三种方式及写法区别
2017/10/27 Javascript
AngularJS实现的生成随机数与猜数字大小功能示例
2017/12/25 Javascript
vue中多路由表头吸顶实现的几种布局方式
2019/04/12 Javascript
VUE脚手架具体使用方法
2019/05/20 Javascript
Vue中实现回车键切换焦点的方法
2020/02/19 Javascript
Vue watch响应数据实现方法解析
2020/07/10 Javascript
python在多玩图片上下载妹子图的实现代码
2013/08/13 Python
python二叉树遍历的实现方法
2013/11/21 Python
python PyTorch预训练示例
2018/02/11 Python
Win10 安装PyCharm2019.1.1(图文教程)
2019/09/29 Python
python Timer 类使用介绍
2020/12/28 Python
HTML5 Canvas阴影使用方法实例演示
2013/08/02 HTML / CSS
Java中实现多态的机制
2015/08/09 面试题
财务管理专业自荐信范文
2013/12/24 职场文书
宠物店的创业计划书范文
2014/01/11 职场文书
小学开学寄语
2014/01/19 职场文书
2014年转正工作总结
2014/11/08 职场文书
2015新年联欢晚会开场白
2014/12/14 职场文书
污水处理保证书
2015/05/09 职场文书
家属联谊会致辞
2015/07/31 职场文书
小学数学国培研修日志
2015/11/13 职场文书
nginx简单配置多个server的方法
2021/03/31 Servers
Pytorch可视化的几种实现方法
2021/06/10 Python