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 HotKeys轻松搞定键盘事件代码
Aug 30 Javascript
js indexOf()定义和用法
Oct 21 Javascript
JS图像无缝滚动脚本非常好用
Feb 10 Javascript
js实现图片无缝滚动
Dec 23 Javascript
javascript中对Date类型的常用操作小结
May 19 Javascript
1秒50万字!js实现关键词匹配
Aug 01 Javascript
JavaScript类数组对象转换为数组对象的方法实例分析
Jul 24 Javascript
详解 微信小程序开发框架(MINA)
May 17 Javascript
vue控制多行文字展开收起的实现示例
Oct 11 Javascript
javascript中可能用得到的全部的排序算法
Mar 05 Javascript
vue项目启动出现cannot GET /服务错误的解决方法
Apr 26 Javascript
用JavaScript实现贪吃蛇游戏
Oct 23 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
用 PHP5 轻松解析 XML
2006/12/04 PHP
PHP教程 基本语法
2009/10/23 PHP
PHP获取Exif缩略图的方法
2015/07/13 PHP
jQuery学习笔记 更改jQuery对象
2012/09/19 Javascript
简单漂亮的js弹窗可自由拖拽且兼容大部分浏览器
2013/10/22 Javascript
非jQuery实现照片散落桌子上,单击放大的LightBox效果
2014/11/28 Javascript
JavaScript中的类(Class)详细介绍
2014/12/30 Javascript
Javascript闭包用法实例分析
2015/01/23 Javascript
JavaScript实现数字数组按照倒序排列的方法
2015/04/06 Javascript
jQuery插件简单实现方法
2015/07/18 Javascript
完美解决jQuery 鼠标快速滑过后,会执行多次滑出的问题
2016/12/08 Javascript
Webpack实现按需打包Lodash的几种方法详解
2017/05/08 Javascript
Vue的Class与Style绑定的方法
2017/09/01 Javascript
微信小程序js文件改变参数并在视图上及时更新【推荐】
2018/06/11 Javascript
JS页面获取 session 值,作用域和闭包学习笔记
2019/10/16 Javascript
jQuery实现图片随机切换、抽奖功能(实例代码)
2019/10/23 jQuery
JavaScript文档加载模式以及元素获取
2020/07/28 Javascript
python处理json数据中的中文
2014/03/06 Python
python检查URL是否正常访问的小技巧
2017/02/25 Python
Python使用修饰器进行异常日志记录操作示例
2019/03/19 Python
Django配置跨域并开发测试接口
2020/11/04 Python
python接口自动化框架实战
2020/12/23 Python
CSS3 实现侧边栏展开收起动画
2014/12/22 HTML / CSS
Right-on官方网站:日本知名的休闲服装品牌
2019/07/12 全球购物
瑞典网上购买现代和复古家具:Reforma
2019/10/21 全球购物
澳大利亚宠物食品和用品商店:PETstock
2020/01/02 全球购物
本科生学习总结的自我评价
2013/10/02 职场文书
进修护士自我鉴定
2013/10/14 职场文书
2014党员四风对照检查材料思想汇报
2014/09/17 职场文书
自查自纠工作总结
2014/10/15 职场文书
2015入党自荐书范文
2015/03/05 职场文书
工程质量保证书
2015/05/09 职场文书
2015年妇产科工作总结
2015/05/18 职场文书
大学自主招生自荐信(2016精选篇)
2016/01/28 职场文书
Nginx域名转发https访问的实现
2021/03/31 Servers
铁头也玩根德 YachtBoy YB-230......
2022/04/05 无线电