代码改变世界

javascript链式调用实现方式总结

2013-11-06 11:41  youxin  阅读(5789)  评论(0编辑  收藏  举报

方法链一般适合对一个对象进行连续操作(集中在一句代码)。一定程度上可以减少代码量,缺点是它占用了函数的返回值。

 

一、方法体内返回对象实例自身(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。

链式调用如下:

 
  1. var obj = new ClassA();  
  2. 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了。

 

链式调用如下:

Js代码  收藏代码
  1. var obj = new ClassB();  
  2. chain(obj)('method1',4)('method2',5)('method3',6); // obj -> prop1=4,prop2=5,prop3=6  

 

第一种方式3次调用后返回了对象自身,这里使用一个空"()"取回对象

Js代码  收藏代码
  1. // result -> prop1=4,prop2=5,prop3=6  
  2. 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中的方法。

转自:http://houfeng0923.iteye.com/blog/1139525