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

  • 相关阅读:
    linux挂载windows共享文件夹
    Cython
    python并行编程
    数据库学习----MySQL 存储引擎
    数据库学习----MySQL 日志
    数据库学习----从文件l数据到数据库
    Golang 学习 ---- 编译打包
    数字转换成千字符
    el-select选择框宽度与输入框相同
    git常用命令总结
  • 原文地址:https://www.cnblogs.com/Langzi127/p/2687930.html
Copyright © 2011-2022 走看看