koa2--delegates模塊源碼解讀

delegates模塊是由TJ大神寫的,該模塊的做用是將內部對象上的變量或函數委託到外部對象上。
而後咱們就能夠使用外部對象就能獲取內部對象上的變量或函數。delegates委託方式有以下:html

getter: 外部對象能夠經過該方法訪問內部對象的值。
setter:外部對象能夠經過該方法設置內部對象的值。
access: 該方法包含getter和setter功能。
method: 該方法能夠使外部對象直接調用內部對象的函數。git

項目文件以下結構:github

|----- 項目
|  |-- delegates.js  # 委託代理的js
|  |-- index.js  # 入口文件的js

1. getter (外部對象能夠經過該方法訪問內部對象的值。)api

使用方法demo以下(index.js):數組

const delegates = require('./delegates');

const obj = {
  xx: {
    name: 'kongzhi',
    age: 30,
    test: function() {
      console.log('xxxxxxx');
    }
  }
};

// 經過delegates將內部對象 xx 委託到外部對象obj上

var d = new delegates(obj, 'xx');
d.getter('name').getter('age').getter('test');

console.log(obj.name); // kongzhi
console.log(obj.age); // 30
obj.test(); // xxxxxxx

2. setter (外部對象能夠經過該方法設置內部對象的值。)app

使用方法的demo以下(代碼在index.js內):函數

const delegates = require('./delegates');

const obj = {
  xx: {
    name: 'kongzhi',
    age: 30,
    test: function() {
      console.log('xxxxxxx');
    }
  }
};

// 經過delegates將內部對象 xx 委託到外部對象obj上

var d = new delegates(obj, 'xx');
d.setter('name').setter('age').setter('test');

// 使用setter後,就能夠在obj對象上直接修改變量或函數的值了
obj.name = '123456';
obj.age = '31';
obj.test = function() {
  console.log('yyyy');
}

/*
 在外部對象obj修改完成後,咱們再使用 外部對象[內部對象][變量] 
 這種方式獲取值, 就能夠看到值更新了
*/
console.log(obj.xx.name); // 123456
console.log(obj.xx.age); // 31
obj.xx.test(); // yyyy

3. access (該方法包含getter和setter功能。)ui

使用方法的demo以下this

const delegates = require('./delegates');

const obj = {
  xx: {
    name: 'kongzhi',
    age: 30,
    test: function() {
      console.log('xxxxxxx');
    }
  }
};

// 經過delegates將內部對象 xx 委託到外部對象obj上

var d = new delegates(obj, 'xx');
d.access('name').access('age').access('test');

// access 該方法既有setter功能,又有getter功能

// 1. 直接使用外部對象 obj, 來訪問內部對象中的屬性
console.log(obj.name); // kongzhi
console.log(obj.age); // 30
obj.test(); // xxxxxxx

// 2. 使用常規的方法獲取對象的內部的屬性
console.log(obj.xx.name); // kongzhi
console.log(obj.xx.age); // 30
obj.xx.test(); // xxxxxxx

// 3. 修改內部對象的屬性
obj.name = '2222';
console.log(obj.name); // 2222
console.log(obj.xx.name); // 2222

4. method (該方法能夠使外部對象直接調用內部對象的函數。)spa

使用方法的demo以下:

const delegates = require('./delegates');

const obj = {
  xx: {
    name: 'kongzhi',
    age: 30,
    test: function() {
      console.log('xxxxxxx');
    }
  }
};

// 經過delegates將內部對象 xx 委託到外部對象obj上

var d = new delegates(obj, 'xx');
d.method('test');

obj.test(); // xxxxxxx

5. fluent

該方法的做用是,若是該方法傳了參數的話,那麼它的含義是修改該變量的值,若是沒有傳入參數的話,那麼
它的做用是獲取該參數的值。

注意:只針對變量有用,若是是函數的話,不建議使用;

以下代碼demo所示:

const delegates = require('./delegates');

const obj = {
  xx: {
    name: 'kongzhi',
    age: 30,
    test: function() {
      console.log('xxxxxxx');
    }
  }
};

// 經過delegates將內部對象 xx 委託到外部對象obj上
var d = new delegates(obj, 'xx');
d.fluent('name').fluent('age');

// 無參數 獲取該對象的值
console.log(obj.name()); // kongzhi
console.log(obj.age()); // 30

// 有參數,就是修改對應的值
obj.name('11111')
obj.age(31)

console.log(obj.xx.name); // 11111
console.log(obj.xx.age); // 31

二:delegates模塊源碼以下:

/**
 * Expose `Delegator`.
 */

module.exports = Delegator;

/**
 * Initialize a delegator.
 *
 * @param {Object} proto
 * @param {String} target
 * @api public
 */
/*
 Delegator 函數接收二個參數,proto指是一個是外部對象,target指外部對象中的一個屬性,也就是內部對象。
 首先判斷this是不是Delegator的實列,若是不是實列的話,就直接使用 new 實列化一下。
 所以 const xx = Delegator(obj, 'xx') 或 const xx = new Delegator(obj, 'xx') 都是能夠的。
 this.proto = proto; 外部對象保存該實列this.proto 中。
 this.target = target; 和proto同樣。
 this.methods = [];
 this.getters = [];
 this.setters = [];
 this.fluents = [];
 如上四個數組做用是 記錄委託了哪些屬性和函數。
*/
function Delegator(proto, target) {
  if (!(this instanceof Delegator)) return new Delegator(proto, target);
  this.proto = proto;
  this.target = target;
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
}

/**
 * Delegate method `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
/*
 method的做用是:該方法能夠使外部對象直接調用內部對象的函數。以下demo:
 const obj = {
  xx: {
    name: 'kongzhi',
    age: 30,
    test: function() {
      console.log('xxxxxxx');
    }
  }
};
// 經過delegates將內部對象 xx 委託到外部對象obj上
var d = new delegates(obj, 'xx');
d.method('test');

obj.test(); // xxxxxxx

1. 首先咱們調用 d.method('test'); 就把該test方法存入 this.methods數組中。
2. 該方法返回了一個函數
obj['test'] = function() {
  return obj['xx']['test'].apply(obj['xx'], arguments);
}
3. 最後返回 this, 返回該實例化對象,目的是能夠鏈式調用。
4. 所以就返回了第二步函數。所以當咱們使用 obj.test() 的時候,就會自動調用該函數。而後
使用 apply方法自動執行 obj['xx']['test'].apply(obj['xx'], arguments); 
*/
Delegator.prototype.method = function(name){
  var proto = this.proto;
  var target = this.target;
  this.methods.push(name);

  proto[name] = function(){
    return this[target][name].apply(this[target], arguments);
  };

  return this;
};

/**
 * Delegator accessor `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
/*
 該方法的做用是包含 getter的做用,同時也包含setter的做用,如demo以下:
 const obj = {
    xx: {
      name: 'kongzhi',
      age: 30,
      test: function() {
        console.log('xxxxxxx');
      }
    }
  };

  // 經過delegates將內部對象 xx 委託到外部對象obj上

  var d = new delegates(obj, 'xx');
  d.access('name').access('age').access('test');

  // access 該方法既有setter功能,又有getter功能

  // 1. 直接使用外部對象 obj, 來訪問內部對象中的屬性
  console.log(obj.name); // kongzhi
  console.log(obj.age); // 30
  obj.test(); // xxxxxxx

  // 2. 使用常規的方法獲取對象的內部的屬性
  console.log(obj.xx.name); // kongzhi
  console.log(obj.xx.age); // 30
  obj.xx.test(); // xxxxxxx

  // 3. 修改內部對象的屬性
  obj.name = '2222';
  console.log(obj.name); // 2222
  console.log(obj.xx.name); // 2222
*/
Delegator.prototype.access = function(name){
  return this.getter(name).setter(name);
};

/**
 * Delegator getter `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
/*
 getter,該方法的做用是:外部對象能夠經過該方法訪問內部對象的值。好比以下demo
 const obj = {
    xx: {
      name: 'kongzhi',
      age: 30,
      test: function() {
        console.log('xxxxxxx');
      }
    }
  };

  // 經過delegates將內部對象 xx 委託到外部對象obj上
  var d = new delegates(obj, 'xx');
  d.getter('name').getter('age').getter('test');

  console.log(obj.name); // kongzhi
  console.log(obj.age); // 30
  obj.test(); // xxxxxxx

  1. 該方法接收一個參數 name, 該參數是一個字符串類型。
  2. 把該參數name值保存到 this.getters數組中。而後咱們使用 __defineGetter__ 監聽對象屬性值的變化。
  想要理解 __defineGetter__ 做用,請看我這篇文章 (https://www.cnblogs.com/tugenhua0707/p/10324983.html#_labe1)
  若是獲取該對象值的話,就會自動調用 __defineGetter__ ,就能監聽到,所以就返回 this[target][name]; 即便:
  obj['xx']['name'];
*/
Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);

  proto.__defineGetter__(name, function(){
    return this[target][name];
  });

  return this;
};

/**
 * Delegator setter `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
/*
 該方法的做用是:外部對象能夠經過該方法設置內部對象的值。使用demo以下:
 const obj = {
    xx: {
      name: 'kongzhi',
      age: 30,
      test: function() {
        console.log('xxxxxxx');
      }
    }
  };

  // 經過delegates將內部對象 xx 委託到外部對象obj上

  var d = new delegates(obj, 'xx');
  d.setter('name').setter('age').setter('test');

  // 使用setter後,就能夠在obj對象上直接修改變量或函數的值了
  obj.name = '123456';
  obj.age = '31';
  obj.test = function() {
    console.log('yyyy');
  }
  
  // 在外部對象obj修改完成後,咱們再使用 外部對象[內部對象][變量] 這種方式獲取值, 就能夠看到值更新了
  console.log(obj.xx.name); // 123456
  console.log(obj.xx.age); // 31
  obj.xx.test(); // yyyy

  1. 一樣的道理,使用 __defineSetter__方法來監聽對象值發生改變,若是對象值發生改變的話,就返回 
  this[target][name] = val; 把值賦值進去。最後返回this對象。
  */
Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);

  proto.__defineSetter__(name, function(val){
    return this[target][name] = val;
  });

  return this;
};

/**
 * Delegator fluent accessor
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
/*
 該方法的做用是,若是該方法傳了參數的話,那麼它的含義是修改該變量的值,若是沒有傳入參數的話,那麼
 它的做用是獲取該參數的值。
 使用demo以下:
 const obj = {
    xx: {
      name: 'kongzhi',
      age: 30,
      test: function() {
        console.log('xxxxxxx');
      }
    }
  };

  // 經過delegates將內部對象 xx 委託到外部對象obj上
  var d = new delegates(obj, 'xx');
  d.fluent('name').fluent('age');

  // 無參數 獲取該對象的值
  console.log(obj.name()); // kongzhi
  console.log(obj.age()); // 30

  // 有參數,就是修改對應的值
  obj.name('11111')
  obj.age(31)

  console.log(obj.xx.name); // 11111
  console.log(obj.xx.age); // 31

  1. 當我像如上demo同樣,使用 d.fluent('name').fluent('age');後,會依次保存到 this.flunts數組中。
  2. 而後返回一個函數,以下代碼:
  obj['name'] = function(val) {
    if ('undefined' != typeof val) {
      this[target][name] = val;
      return this;
    } else {
      return this[target][name];
    }
  }
  若是值沒有傳遞電話,就直接返回 this[target][name]; 即:obj['xx']['name'];
  若是傳遞了值的話,就把值賦值到對象裏面去,如代碼:this[target][name] = val; 即:obj['xx']['name'] = val;
*/
Delegator.prototype.fluent = function (name) {
  var proto = this.proto;
  var target = this.target;
  this.fluents.push(name);

  proto[name] = function(val){
    if ('undefined' != typeof val) {
      this[target][name] = val;
      return this;
    } else {
      return this[target][name];
    }
  };

  return this;
};
相關文章
相關標籤/搜索