方法鏈一般適合對一個對象進行連續操作(集中在一句代碼)。一定程度上可以減少代碼量,缺點是它占用了函數的返回值。
一、方法體內返回對象實例自身(this)
function ClassA(){ this.prop1 = null; this.prop2 = null; this.prop3 = null; } ClassA.prototype = { method1 : function(p1){ this.prop1 = p1; return this; }, method2 : function(p2){ this.prop2 = p2; return this; }, method3 : function(p3){ this.prop3 = p3; return this; } }
定義了function/類ClassA。有三個屬性/字段prop1,prop2,prop3,三個方法methed1,method2,method3分別設置prop1,prop2,prop3。
鏈式調用如下:
- var obj = new ClassA();
- obj.method1(1).method2(2).method3(3); // obj -> prop1=1,prop2=2,prop3=3
可以看到對obj進行了連續三次操作,只要願意ClassA的N多方法都這樣定義,調用鏈會一直延續。
該方式缺點是鏈方法唯一地綁定於一種對象類型(ClaaaA),按這種方式實現鏈式操作,每定義一個類,其方法體中都要返回this。第二種方式可以解決這個問題。
二、對象傳入后每次調用返回函數自身
/** * chain 最易讀版 * @param {Object} obj */ function singleChain1(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; } /** * chain 易讀版 * @param {Object} obj */ function singleChain2(obj){ return function(){ var Self = arguments.callee; Self.obj = obj; if(arguments.length==0){ return Self.obj; } var methodName = arguments[0], methodArgs = [].slice.call(arguments,1); Self.obj[methodName].apply(Self.obj,methodArgs); return Self; } } /** * chain 精簡版 * @param {Object} obj */ function singleChain3(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; } }
使用:
/** * 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; } } //定義的function/類ClassB 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; } }
注意ClassB的method1,method2,method3中不再返回this了。
鏈式調用如下:
- var obj = new ClassB();
- chain(obj)('method1',4)('method2',5)('method3',6); // obj -> prop1=4,prop2=5,prop3=6
第一種方式3次調用后返回了對象自身,這里使用一個空"()"取回對象
- // result -> prop1=4,prop2=5,prop3=6
- var result = chain(obj)('method1',4)('method2',5)('method3',6)();
這種方式寫類時方法體中無須返回this,且可以對任何對象進行鏈式調用。
接下來介紹YUI中Node類實現的鏈式調用方法。
在YUI3中,Node類的基礎是Dom,很多Node類的方法都是調用Dom類的同名方法,如上面提到的setAttribute、setStyle等,
在Dom類源碼中也未設置返回本對象,在Node類提供了importMethods方法來導入Dom中相同的方法並支持鏈式調用。示例代碼如下:
//Dom類及靜態方法 function Dom(id){ this.dom = document.getElementById(id); } Dom.setStyle = function(node,name,value){ node.dom.style[name] = value; } Dom.setAttribute = function(node,name,v){ node.dom.setAttribute(name,v); } //Node類 function Node(id){ this.dom = document.getElementById(id); } //添加方法的函數 Node.addMethod = function(method,fn){//,context Node.prototype[method] = function(){ var me = this; //Array.prototype.unshift.call(arguments,me); //or arguments = Array.prototype.slice.call(arguments); arguments.unshift(me); fn.apply(me,arguments); return me; } } //批量添加方法的函數 Node.importMethods = function(host,methods){ for(var i in methods){ var m = methods[i]; var fn = host[m]; Node.addMethod(m,fn); } } //定義需要給Node類添加的方法名 列表 var methods = ['setStyle','setAttribute']; //使用批量添加方法的函數將Dom中的相關方法添加到Node中 Node.importMethods(Dom,methods); //可以在Node中進行鏈式調用 var n = new Node('log').setStyle('border','2px solid red').setAttribute('t',22);
在實際使用中,可以對原有的對象方法(如Dom中的方法)擴展到另一個類中(如Node類),在Node類中進行鏈式調用。當然也可以使用同樣的方式(importMethods)不擴展而是覆蓋Dom中的方法。

