《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);
 
posted @ 2015-03-22 23:46  oadaM92  阅读(193)  评论(0编辑  收藏  举报