代码改变世界

koa2--delegates模块源码解读

2019-03-20 00:05  龙恩0707  阅读(598)  评论(0编辑  收藏  举报

delegates模块是由TJ大神写的,该模块的作用是将内部对象上的变量或函数委托到外部对象上。
然后我们就可以使用外部对象就能获取内部对象上的变量或函数。delegates委托方式有如下:

getter: 外部对象可以通过该方法访问内部对象的值。
setter:外部对象可以通过该方法设置内部对象的值。
access: 该方法包含getter和setter功能。
method: 该方法可以使外部对象直接调用内部对象的函数。

项目文件如下结构:

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

1. getter (外部对象可以通过该方法访问内部对象的值。)

使用方法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 (外部对象可以通过该方法设置内部对象的值。)

使用方法的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功能。)

使用方法的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.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 (该方法可以使外部对象直接调用内部对象的函数。)

使用方法的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;
};