Javascript中chaining的实现

     如果你了解Jquery,你一定为它的chaining的便利而折服。而它的原理,其实也很简单,不过是每个方法都返回this对象而已。如下: 

( function ($) {
     function _$(elements) {
         this._elements = [];
         for ( var i = 0; i < elements.length; i++) {
             if ( typeof (elements[i]) == "string") {
                 this._elements.push(document.getElementById(elements[i]));
            }  else {
                 this._elements.push(elements[i]);
            }
        }
    }

    _$.prototype = {
        each:  function (fn) {
             if (arguments.length == 1) {
                 for ( var i = 0; i <  this._elements.length; i++) {
                    fn.call( this._elements[i]);
                }
            }  else {
                 var argumentArr = [];
                 for ( var i = 1; i < arguments.length; i++) {
                    argumentArr[i - 1] = arguments[i];
                }
                 for ( var i = 0; i <  this._elements.length; i++) {
                    fn.apply( this._elements[i], argumentArr);
                }
            }

             return  this;
        },
        setStyle:  function (attribute, attributeValue) {
             var _setStyle =  function (attribute, attributeValue) {
                 this.style[attribute] = attributeValue;
            };
             return  this.each(_setStyle, attribute, attributeValue);
        },
        display:  function () {
             return  this.setStyle("display", "block");
        },
        hide:  function () {
             return  this.setStyle("display", "none");
        },
        addEvent:  function (type, fn) {
             var _addEvent =  function (type, fn) {
                 if (window.addEventListener) {
                     this.addEventListener(type, fn,  false);
                }  else  if (window.attachEvent) {
                     this.attachEvent('on' + type, fn);
                }
            };
             return  this.each(_addEvent, type, fn);
        },
        removeEvent:  function (type, fn) {
             var _removeEvent =  function (type, fn) {
                 if (window.removeEventListener) {
                     this.removeEventListener(type, fn,  false);
                }  else  if (window.detachEvent) {
                     this.detachEvent('on' + type, fn);
                }
            };
             return  this.each(_removeEvent, type, fn);
        }
    };

    window[$] =  function () {
         return  new _$(arguments);
    };

} ("$")); 

是不是一个简单版的Jquery? 它不过是先在闭包中先定义一个内部类,同时类的构造器传值一个或多个dom元素的id或者类实体对象(这里只是简写,并没有Juery那么复杂)。然后通过扩展内部类的prototype属性,实现一系列方法。在这堆方法里,最核心的是each方法了,它完美的使用了call和apply,然后有容乃大的显式接受一个funciton指针参数。另外,还有1个类似于Jquery的方式就是闭包最外层传递的$参数值了。 实际上,它可以是任意的合法js变量名。

 

     看看调用方式吧,有它一切你都会懂得:

< html >
< head >
</ head >
< body >
     < div  id ="div1" >this is div1. </ div >
     < div  id ="div2" >this is div2. </ div >
     < script  type ="text/javascript"  src ="chaining.js" ></ script >
     < script  type ="text/javascript" >
        
var  sayHello  =   function  () {
            alert(
' hello, I am is  '   +   this .id);
        }

        $(
' div1 ' ' div2 ' ).setStyle( " color " " red " ).addEvent( " click " , sayHello);
        $(
' div2 ' ).setStyle( " color " " yellow " ).removeEvent( " click " , sayHello);
    
</ script >
</ body >

</html> 

关键点在于以$开头的那2行啊,是不是果然很Jquery? :)

 

     而如果要作为一个好的Js Library,仅仅这么做,是不是还不够完美?比如,如果我想不用$作为限定操作符,将控制权完全交给Library用户。改进如下: 

( function () {
     function _$(elements) {
         this._elements = [];
         for ( var i = 0; i < elements.length; i++) {
             if ( typeof (elements[i]) == "string") {
                 this._elements.push(document.getElementById(elements[i]));
            }  else {
                 this._elements.push(elements[i]);
            }
        }
    }

    _$.prototype = {
        each:  function (fn) {
             if (arguments.length == 1) {
                 for ( var i = 0; i <  this._elements.length; i++) {
                    fn.call( this._elements[i]);
                }
            }  else {
                 var argumentArr = [];
                 for ( var i = 1; i < arguments.length; i++) {
                    argumentArr[i - 1] = arguments[i];
                }
                 for ( var i = 0; i <  this._elements.length; i++) {
                    fn.apply( this._elements[i], argumentArr);
                }
            }

             return  this;
        },
        setStyle:  function (attribute, attributeValue) {
             var _setStyle =  function (attribute, attributeValue) {
                 this.style[attribute] = attributeValue;
            };
             return  this.each(_setStyle, attribute, attributeValue);
        },
        display:  function () {
             return  this.setStyle("display", "block");
        },
        hide:  function () {
             return  this.setStyle("display", "none");
        },
        addEvent:  function (type, fn) {
             var _addEvent =  function (type, fn) {
                 if (window.addEventListener) {
                     this.addEventListener(type, fn,  false);
                }  else  if (window.attachEvent) {
                     this.attachEvent('on' + type, fn);
                }
            };
             return  this.each(_addEvent, type, fn);
        },
        removeEvent:  function (type, fn) {
             var _removeEvent =  function (type, fn) {
                 if (window.removeEventListener) {
                     this.removeEventListener(type, fn,  false);
                }  else  if (window.detachEvent) {
                     this.detachEvent('on' + type, fn);
                }
            };
             return  this.each(_removeEvent, type, fn);
        }
    };

    window.installHelper =  function (scrope, interfaceName) {
        scrope[interfaceName] =  function () {
             return  new _$(arguments); 
        }
    };

} ()); 

调用方式为: 

 window.installHelper(window, "$");

这里的两个参数可以按照需要传值。比如,如果你定义了一个com命名空间,然后想将上述chaining类库绑定到com.util上,那么执行:

window.installHelper(window.com, "util");

即可了。使用时,实例如下:

         var $ = com.util;
        $('div1', 'div2').setStyle("color", "red").addEvent("click", sayHello);

        $('div2').setStyle("color", "yellow").removeEvent("click", sayHello); 

注意,上述$非必须,你可以使用其他任意变量名,我只是偷懒而已。

 

     是不是觉得很闷骚? 呵呵,这还不够撒!为了说明问题,我列举一个稍微有点极端的简单例子: 

function Api(){
     this.name = "Hello world";
}
Api.prototype = ( function () {
     return {
        setName:  function (name) {
             this.name = name;
             return  this;
        },
        getName:  function () {
             return  this.name;
        },
        getMessage:  function () {
            console.log( this.name);
             return  this;
        }
    };
})();     

var api =  new Api();

api.getMessage().setName("changed").getMessage().getName(); 

在setName(name)方法里返回对象自身,我们可以接受。但是,在访问器getName()里呢?我们要的是对象属性值,不是对象自身,所以只返回name值。但是,调用getName()后,chaining就断了! 怎么办?

 

        借鉴于Jsonp,我们也可搞个回调函数玩玩:

function Api2(name) {
     this.name = "Hello World";
}

Api2.prototype = ( function () {
     return {
        setName:  function (name) {
             this.name = name;
             return  this;
        },
        getName:  function (callback) {
            callback.call( thisthis.name);
             return  this;
        },
        getMessage:  function () {
            console.log( this.name);
             return  this;
        }
    };
})();

var api2 =  new Api2();
var callback =  function(name){
    console.log(name);
};

api2.getMessage().setName("changed").getMessage().getName(callback).setName("changed2").getMessage(); 

至此就可以无限的chaining了!

 

     本文和所有其他本人的Pro Javascript Design Pattern系列全部文章均基于Pro Javascript Design Pattern的思想,并修正书中的一些源码错误。 源码download

转载于:https://www.cnblogs.com/Langzi127/archive/2012/09/16/2687930.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值