zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Android 解决小米手机Android Studio安装app 报错的问题It is possible that this issue is resolved by uninstalling an existi
    Android Unresolved Dependencies
    Android studio 自定义打包apk名
    Android Fragment与Activity交互的几种方式
    魅族和三星Galaxy 5.0webView 问题Android Crash Report
    Android几种常见的多渠道(批量)打包方式介绍
    Android批量打包 如何一秒内打完几百个apk渠道包
    上周热点回顾(9.30-10.6)团队
    上周热点回顾(9.23-9.29)团队
    上周热点回顾(9.16-9.22)团队
  • 原文地址:https://www.cnblogs.com/Langzi127/p/2687930.html
Copyright © 2011-2022 走看看