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实现的鼠标链接提示效果生成器代码
Jun 28 Javascript
实用的Jquery选项卡TAB示例代码
Aug 28 Javascript
读取input:file的路径并显示本地图片的方法
Sep 23 Javascript
jquery 字符串切割函数substring的用法说明
Feb 11 Javascript
node.js 使用ejs模板引擎时后缀换成.html
Apr 22 Javascript
js HTML5多媒体影音播放
Oct 17 Javascript
微信小程序 获取微信OpenId详解及实例代码
Oct 31 Javascript
Bootstrap组件之下拉菜单,多级菜单及按钮布局方法实例
May 25 Javascript
Node.js+Express+MySql实现用户登录注册功能
Jul 10 Javascript
原生js实现的移动端可拖动进度条插件功能详解
Aug 15 Javascript
vue日历/日程提醒/html5本地缓存功能
Sep 02 Javascript
antd design table更改某行数据的样式操作
Oct 31 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
php无限遍历目录示例
2014/02/21 PHP
php随机生成数字字母组合的方法
2015/03/18 PHP
PHP PDOStatement::columnCount讲解
2019/01/30 PHP
javawscript 三级菜单的实现原理
2009/07/01 Javascript
Node.js开源应用框架HapiJS介绍
2015/01/14 Javascript
jQuery模拟360浏览器切屏效果幻灯片(附demo源码下载)
2016/01/29 Javascript
JS实现一次性弹窗的方法【刷新后不弹出】
2016/12/26 Javascript
微信小程序 Nginx环境配置详细介绍
2017/02/14 Javascript
js实现下一页页码效果
2017/03/07 Javascript
JavaScript编程设计模式之观察者模式(Observer Pattern)实例详解
2017/10/25 Javascript
javascript高仿热血传奇游戏实现代码
2018/02/22 Javascript
redux.js详解及基本使用
2019/05/24 Javascript
Cordova(ionic)项目实现双击返回键退出应用
2019/09/17 Javascript
Vue.js自定义指令学习使用详解
2019/10/19 Javascript
vue实现简易计算器功能
2021/01/20 Vue.js
python中的hashlib和base64加密模块使用实例
2014/09/02 Python
python语言使用技巧分享
2016/05/31 Python
python异常和文件处理机制详解
2016/07/19 Python
Python实现判断一行代码是否为注释的方法
2018/05/23 Python
python列表list保留顺序去重的实例
2018/12/14 Python
python查询文件夹下excel的sheet名代码实例
2019/04/02 Python
python中调试或排错的五种方法示例
2019/09/12 Python
屈臣氏马来西亚官网:Watsons马来西亚
2019/06/15 全球购物
Java里面Pass by value和Pass by Reference是什么意思
2016/05/02 面试题
多媒体编辑专业毕业生推荐信
2013/11/05 职场文书
电大本科自我鉴定
2014/02/05 职场文书
汇报材料怎么写
2014/12/30 职场文书
2015年秋季学校开学标语
2015/07/16 职场文书
2015年计算机教师工作总结
2015/07/22 职场文书
2016年五一劳动节专题校园广播稿
2015/12/17 职场文书
《你在为谁工作》心得体会(共8篇)
2016/01/20 职场文书
MySQL Shell的介绍以及安装
2021/04/24 MySQL
教你怎么用Python监控愉客行车程
2021/04/29 Python
SpringBoot SpringEL表达式的使用
2021/07/25 Java/Android
Python编程源码报错解决方法总结经验分享
2021/10/05 Python
Win11 Build 25179预览版发布(附更新内容+ISO官方镜像下载)
2022/08/14 数码科技