锁定老帖子 主题:JavaScript中链式调用之研习
该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-08-30
clue 写道
zhouyrt 写道
二、对象传入后每次调用返回函数自身
/** * chain 精简版 * @param {Object} obj */ function chain(obj){ return function(){ var Self = arguments.callee; Self.obj = obj; if(arguments.length==0){ return Self.obj; } Self.obj[arguments[0]].apply(Self.obj,[].slice.call(arguments,1)); return Self; } }
第二种方式还是第一次见,很巧妙的用法
不过可以简化一下,不使用callee,直接利用闭包: function chain(obj){ var fn = function(method){ if(arguments.length <= 0){ return fn; } var args = Array.prototype.slice.call(arguments, 1); obj[method].apply(obj, args); return fn; } return fn; } PS:那个[].slice的用法还真是。。。够短,不过总觉得凭白无故创建一个数组只为借它的一个方法用用,有点…… 可以Array.prototype.slice...... |
|
返回顶楼 | |
发表时间:2010-08-31
我来写个小小的注释吧
/** * 精简版 * @param {Object} obj */ function chain(obj){ //执行chain直接返回一个方法(类),这样执行chain(obj)('method1',4)就相当于执行这个方法,参数是('method1',4) return function(){ //定义Self = 本方法的调用者 var Self = arguments.callee; //给调用者一个属性 即obj参数,这样每次链式调用的时候就可以获得最初的那个obj,而这个obj正好是被链式调用的那个方法,这里是classB Self.obj = obj; //一个参数都没有,这是链式调用结束的时候发生的事情,即:...('method3',6)()这里的最后一个括号,此时返回最初的那个obj,这里是classB if(arguments.length==0){ return Self.obj; } /*执行Self.obj[arguments[0]] 比如我们第一次链式调用的时候:('method1',4) 这里我们通过 Self.obj[arguments[0]] 取得了method1方法, Self.obj[arguments[0]].apply(Self.obj,[].slice.call(arguments,1)); 代表把Self.obj[arguments[0]]当做Self.obj的一个方法执行, 参数是后面的[].slice.call(arguments,1) [].slice.call(arguments,1) 截取arguments数组,从索引第一个截取到最后 */ Self.obj[arguments[0]].apply(Self.obj,[].slice.call(arguments,1)); //返回Self return Self; } } function ClassB(){ this.prop1 = null; this.prop2 = null; this.prop3 = null; } ClassB.prototype = { method1 : function(p1){ this.prop1 = p1; }, method2 : function(p2){ this.prop2 = p2; }, method3 : function(p3){ this.prop3 = p3; } } var obj = new ClassB(); chain(obj)('method1',4)('method2',5)('method3',6); // obj -> prop1=4,prop2=5,prop3=6 var result = chain(obj)('method1',4)('method2',5)('method3',6)(); // result -> prop1=4,prop2=5,prop3=6 |
|
返回顶楼 | |
发表时间:2010-09-02
最后修改:2010-09-02
挺巧妙的~
但看上去有点繁琐 |
|
返回顶楼 | |
发表时间:2010-09-02
费解啊 发现自己js学的还是不好
|
|
返回顶楼 | |
发表时间:2010-09-02
最后修改:2010-09-02
其实,这是一个降低代码可读性的典型反面教材……
如果嫌在每个方法最后返回this是重复代码,那大可以实现一个wrap函数,用AOP的思路来对需要链式调用的方法进行包装: function chain(obj, methods){ for(var i=0; i<methods.length; i++){ var m = methods[i]; obj[m] = (function(old){ return function(){ old.apply(obj, arguments); return obj; } })(obj[m]) } } 有了上面的包装函数,就可以这样使用: function Test(){} Test.prototype = { methodA: function(){ alert('methodA'); }, methodB: function(arg){ alert(arg); } } var test = new Test(); //声明需要链式调用的方法 chain(test, ['methodA', 'methodB']); //try it:) test.methodB('chain').methodA() 抛砖引玉,你还可以让chain去修改构造函数(比如这里的Test)的prototype,这样只需要设置一次,所有的实例都具有链式调用的能力了,是不是和rails中的dsl有点类似呢?呵呵…… 总之,我们在写代码或者设计API的时候,首要的原则就是要保证代码的可读性,SICP里有句话说的好:“程序是写给人看的,机器顺便执行”。 |
|
返回顶楼 | |
发表时间:2010-09-03
最后修改:2010-09-03
笨笨狗 写道 其实,这是一个降低代码可读性的典型反面教材……
如果嫌在每个方法最后返回this是重复代码,那大可以实现一个wrap函数,用AOP的思路来对需要链式调用的方法进行包装: function chain(obj, methods){ for(var i=0; i<methods.length; i++){ var m = methods[i]; obj[m] = (function(old){ return function(){ old.apply(obj, arguments); return obj; } })(obj[m]) } } 有了上面的包装函数,就可以这样使用: function Test(){} Test.prototype = { methodA: function(){ alert('methodA'); }, methodB: function(arg){ alert(arg); } } var test = new Test(); //声明需要链式调用的方法 chain(test, ['methodA', 'methodB']); //try it:) test.methodB('chain').methodA() 抛砖引玉,你还可以让chain去修改构造函数(比如这里的Test)的prototype,这样只需要设置一次,所有的实例都具有链式调用的能力了,是不是和rails中的dsl有点类似呢?呵呵…… 总之,我们在写代码或者设计API的时候,首要的原则就是要保证代码的可读性,SICP里有句话说的好:“程序是写给人看的,机器顺便执行”。 To 笨笨狗 首先,谢谢您给我戴上“降低代码可读性的典型反面教材”的帽子。既然是教材,虽然是反面的,仍然可以给旁人以警惕作用。 其次,我想说的是 1,代码可读性与精简的代码并不必然关系,可以慢慢多琢磨下。 2, 代码可读性与代码风格也不必然关系,多数喜欢的可能是过程式/面向对象式,为什么一尝试函数式就那么不习惯,不喜欢。 难道这样写才算“提高代码可读性” if(a){ fn1(); }else{ fn2(); } 这样写就“降低代码可读性” a ? fn1() : fn2(); 3, 我承认我们应该写更良好风格的代码,我也曾经深受自己代码风格不良好之苦并幡然醒悟,但上面这个问题的本质,真的是追求更具可读性的代码风格吗? 顺便提下,其实回复 clue,tangjikey,ccyingfu时也说了附件中有chain的“易读版”,“最易读版”。这里贴出 /** * chain 最易读版 * @param {Object} obj */ function singleChain(obj){ function chain(){ if (arguments.length == 0){ return chain.obj; } var methodName = arguments[0], methodArgs = [].slice.call(arguments,1); chain.obj[methodName].apply(chain.obj,methodArgs); return chain; } chain.obj = obj; return chain; } 组织代码的风格千千万万,按需权衡。 最后,感谢您的见解! |
|
返回顶楼 | |
发表时间:2010-09-03
最后修改:2010-09-03
zhouyrt 写道 To 笨笨狗 首先,谢谢您给我戴上“降低代码可读性的典型反面教材”的帽子。既然是教材,虽然是反面的,仍然可以给旁人以警惕作用。 其次,我想说的是 1,代码可读性与精简的代码并不必然关系,可以慢慢多琢磨下。 2, 代码可读性与代码风格也不必然关系,多数喜欢的可能是过程式/面向对象式,为什么一尝试函数式就那么不习惯,不喜欢。 难道这样写才算“提高代码可读性” if(a){ fn1(); }else{ fn2(); } 这样写就“降低代码可读性” a ? fn1() : fn2(); 3, 我承认我们应该写更良好风格的代码,我也曾经深受自己代码风格不良好之苦并幡然醒悟,但上面这个问题的本质,真的是追求更具可读性的代码风格吗? 顺便提下,其实回复 clue,tangjikey,ccyingfu时也说了附件中有chain的“易读版”,“最易读版”。这里贴出 /** * chain 最易读版 * @param {Object} obj */ function singleChain(obj){ function chain(){ if (arguments.length == 0){ return chain.obj; } var methodName = arguments[0], methodArgs = [].slice.call(arguments,1); chain.obj[methodName].apply(chain.obj,methodArgs); return chain; } chain.obj = obj; return chain; } 组织代码的风格千千万万,按需权衡。 最后,感谢您的见解! 楼主不必发火,我没有给你带任何帽子的目的:)其实我说说的“可读性”是针对api的使用者,而不是针对api的具体实现,也就是说我们的关注点在于相比 chain(obj)(method1,arg1)(method2,arg2)(method3,arg3) 下面这种风格 obj.method1(arg1).method2(arg2).method3(arg3) 更容易阅读,也更容易理解,不会打断读者的思维,不是么? 当然,话说回来,对于初学者来说,多多了解不同的实现方式那是非常必要的,这方面的推荐去研究Prototype.js的源码,里面对高阶函数的运用可以称得上“炉火纯青”,另外,它的OO类模拟机制是非常新颖的,真让人眼前一亮:) |
|
返回顶楼 | |
发表时间:2010-09-03
另外,说起这个chaing call,不得不推荐dustin diaz的这篇文章:
Asynchronous method queue chaining in JavaScript http://www.dustindiaz.com/async-method-queues/ 异步链式调用更为有趣,也更为实用:) |
|
返回顶楼 | |
发表时间:2010-09-03
最后修改:2010-09-07
To 笨笨狗,
探讨,学习,进步。 谢谢! |
|
返回顶楼 | |
发表时间:2010-09-07
最后修改:2010-09-07
第一种方法我经常用,有的时候在JAVA中也大量使用,比如构建SQL,URL,或者JSON,或者DataModel 比较繁琐的时候,一般会用
Hql hql = new Hql("from UserInfo o where o.xx = ? ",xx).append("and o.userName = ? and o.yy = ? ",userName,yy) ; return orm().findFirst(hql); new UrlEngine(request).remove("arg1").append("arg2",arg2).append("arg3",arg3).toString() ; new JSONMap().append("k1",val1).appendList("list1").add("list item1").parentMap() new AppDataModel().setEntity(entity).append("user",user).setRows(entity.getDepts()).setPager(pager); |
|
返回顶楼 | |