zoukankan      html  css  js  c++  java
  • 《javascript设计模式》笔记之第六章:方法的链式调用

    这一章要实现的就是jQuery的那种链式调用,例子:

    $(this).setStyle('color', 'green').show();
    
    一:调用链的结构
    首先我们来看一下最简单的$()函数的实现:
    function $() {
      var elements = [];
      for (var i = 0, len = arguments.length; i < len; ++i) {
        var element = arguments[i];
        if (typeof element == 'string') {
          element = document.getElementById(element);
        }
        if (arguments.length == 1) {
          return element;
        }
        elements.push(element);
      }
      return elements;
    }
    
    如果我们要实现$().method()这样的使用方法,想到应该就是$()函数有返回值,并且返回的是一个对象。要实现前面的所说的,最好的方法就是$()里面应该有个类可以让我们new一个对象出来,所以上面的代码就变成下面的样子:
    (function() {
      // Use a private class.
      function _$(els) {
        this.elements = [];
        for (var i = 0, len = els.length; i < len; ++i) {
          var element = els[i];
          if (typeof element == 'string') {
            element = document.getElementById(element);
          }
          this.elements.push(element);
        }
      }
      // The public interface remains the same.
      window.$ = function() {
        return new _$(arguments);
      };
    })();
    
    此外我们的对象应该要有方法可以调用啊,那就定义在prototype上吧,然后代码就变成下面那样了:
    (function() {
      function _$(els) {
        // ...
      }
      _$.prototype = {
        each: function(fn) {
          for ( var i = 0, len = this.elements.length; i < len; ++i ) {
            fn.call(this, this.elements[i]);
          }
          return this;
        },
        setStyle: function(prop, val) {
          this.each(function(el) {
            el.style[prop] = val;
          });
          return this;
        },
        show: function() {
          var that = this;
          this.each(function(el) {
            that.setStyle('display', 'block');
          });
          return this;
        },
        addEvent: function(type, fn) {
          var add = function(el) {
            if (window.addEventListener) {
              el.addEventListener(type, fn, false);
            } 
            else if (window.attachEvent) {
              el.attachEvent('on'+type, fn);
            }
          };
          this.each(function(el) {
            add(el);
          });
          return this;
        }
      };
      window.$ = function() {
        return new _$(arguments);
      };
    })();
    
    到这里,我们就可以大概的使用一下了:
      $('test-1', 'test-2').show().
        setStyle('color', 'red').
        addEvent('click', function(e) {
          $(this).setStyle('color', 'green');
        });
    
    二:设计一个支持方法链式调用的JavaScript库
    一般的js库里面都包含着事件、DOM、Ajax这几个重要的模块,那模仿一下:
    Function.prototype.method = function(name, fn) {
      this.prototype[name] = fn;
      return this;
    };
    (function() {
      function _$(els) {
        // ...
      }
      /*    Events      * addEvent      * getEvent  */
      _$.method('addEvent', function(type, fn) {
        // ...
      }).method('getEvent', function(e) {
        // ...
      }).
      /*    DOM      * addClass      * removeClass      * replaceClass      * hasClass      * getStyle      * setStyle  */
      method('addClass', function(className) {
        // ...
      }).method('removeClass', function(className) {
        // ...
      }).method('replaceClass', function(oldClass, newClass) {
        // ...
      }).method('hasClass', function(className) {
        // ...
      }).method('getStyle', function(prop) {
        // ...
      }).method('setStyle', function(prop, val) {
        // ...
      }).
      /*    AJAX      * load. Fetches an HTML fragment from a URL and inserts it into an element.  */
      method('load', function(uri, method) {
        // ...
      });
      window.$ = function() {
        return new _$(arguments);
      });
    })();
    
    其实用的还是上面的方法。
    不过,注意到最上面的几行代码了吗,我记得第一章说过,这样的话让添加prototype方法也使用链式调用玩起来了。。
    还有,倒数第四行那里,我们总是把函数赋予给window.$,这样的话与一些使用$做命名空间的库是有冲突的,例如和jQuery有冲突~,那换命名空间吧,下面就实现一个安装器来方便改变命名空间:
    Function.prototype.method = function(name, fn) {
      // ...
    };
    (function() {
      function _$(els) {
        // ...
      }
      _$.method('addEvent', function(type, fn) {
        // ...
      })
      // ...
        
      window.installHelper = function(scope, interface) {
        scope[interface] = function() {
          return new _$(arguments);
        }
      };
    })();
    
    上面代码的主要思想就是先不给任何全局变量赋值,只有调用installHelper绑定相应的命名空间之后才能使用。
    使用方法如下:
    installHelper(window, '$');
    
    $('example').show();
    
    这样就等于把函数赋值给了window.$了。
    三:使用回调从支持链式调用的方法获取数据
    上面说的那些链式方法都是返回this的,如果方法本身要返回值了?那么和返回this不就冲突了吗?所以对于这种情况,我们要想出一种方法,那就是对于这种情况,我们传入一个回调函数,来调用返回来的数值,之后还是要返回this的~
    例子:
    window.API2 = window.API2 || {};
    API2.prototype = function() {
      var name = 'Hello world';
      // Privileged mutator method.
      setName: function(newName) {
        name = newName;
        return this;
      },
      // Privileged accessor method.
      getName: function(callback) {
        callback.call(this, name);
        return this;
      }
    }();
    
    上面代码只要看getName哪里就可以了,传入了一个回调函数,然后取出值来直接调用,之后还是返回this,就不破坏链式调用了。
    上面代码的一个使用方法:
    var o2 = new API2;
    o2.getName(console.log).setName('Meow').getName(console.log);
    
     
  • 相关阅读:
    2019南昌网络赛-I(单调栈+线段树)
    poj3250(单调栈模板题)
    poj2528(线段树+离散化)
    poj2828(线段树查找序列第k小的值)
    Seikimatsu Occult Tonneru(网络流,状态数(建不建边)不多时,可考虑直接进行枚举
    A. Coffee Break(思维题,类似于邻接表的head数组用法)
    E. Paint the Tree(树形dp)
    cdq分治学习
    2018SEERC Points and Rectangles (CDQ分治)
    SEERC 2018 Inversion
  • 原文地址:https://www.cnblogs.com/oadaM92/p/4358497.html
Copyright © 2011-2022 走看看