koa 用到的delegates NPM包源码阅读

介绍

delegates,可以帮我们方便快捷地使用设计模式当中的委托模式(Delegation Pattern),即外层暴露的对象将请求委托给内部的其他对象进行处理。

用法

delegates 基本用法就是将内部对象的变量或者函数绑定在暴露在外层的变量上,直接通过 delegates 方法进行如下委托,基本的委托方式包含:

  • getter:外部对象可以直接访问内部对象的值
  • setter:外部对象可以直接修改内部对象的值
  • access:包含 getter 与 setter 的功能
  • method:外部对象可以直接调用内部对象的函数
const delegate = require('delegates'); 
​
const petShop = {
 dog: {
   name: '旺财',
   age: 1,
   sex: '猛汉',
   bar() {
     console.log('bar!');
   }
 },
}
复制代码

​ // 将内部对象 dog 的属性、函数委托至暴露在外的 petShop 上

delegates(petShop, 'dog')
 .getter('name')
 .setter('age')
 .access('sex')
 .method('bar');
​
// 访问内部对象属性
console.log(petShop.name)
// => '旺财'
​
// 修改内部对象属性
petShop.age = 2;
console.log(petShop.dog.age)
// => 2
​
// 同时访问和修改内部对象属性
console.log(petShop.sex)
// => '猛汉'
petShop.sex = '公主';
console.log(petShop.sex);
// => '公主'
​
// 调用内部对象函数
petShop.bar();
// 'bar!'
复制代码

除了上面这种方式之外,还可以在外部对象上添加类似 jQuery 风格的函数,即:

  • 函数不传参数的时候,获取对应的值
  • 函数传参数的时候,修改对应的值
const delegate = require('delegates');
​
const petShop = {
 dog: {
   name: '旺财',
 },
}
​
delegates(petShop, 'dog')
 .fluent('name');
​
// 不传参数,获取内部属性
console.log(petShop.name());
​
// 传参数,修改内部属性
// 还可以链式调用
console.log(
 petShop.name('二哈')
 .name('蠢二哈')
 .name();
);
复制代码

源码学习

源代码比较简洁。

初始化

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 = [];
}
复制代码

this 对象中 methods | getters | setters | flaunts 均为数组,用于记录委托了哪些属性和函数。

上述初始化函数的第一行值得引起注意: 如果 this 不是 Delegator 的实例的话,则调用 new Delegator(proto, target)。通过这种方式,可以避免在调用初始化函数时忘记写 new 造成的问题,因为此时下面两种写法是等价的:

  • let x = new Delegator(petShop, 'dog')
  • let x = Delegator(petShop, 'dog')

getter

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;
};
复制代码

上面代码中的关键在于 __defineGetter__ 的使用,它可以在已存在的对象上添加可读属性,其中第一个参数为属性名,第二个参数为函数,返回值为对应的属性值:

const obj = {};
obj.__defineGetter__('name', () => 'elvin');
​
console.log(obj.name);
// => 'elvin'
​
obj.name = '旺财';
console.log(obj.name);
// => 'elvin'
复制代码

需要注意的是尽管 __defineGetter__ 曾被广泛使用,但是已不被推荐,建议通过 Object.defineProperty 实现同样功能,或者通过 get 操作符实现类似功能:

const obj = {};
Object.defineProperty(obj, 'name', {
   value: 'elvin',
});
​
Object.defineProperty(obj, 'sex', {
 get() {
   return 'male';
 }
});
​
const dog = {
 get name() {
   return '旺财';
 }
};
复制代码

Github 上已有人提出相应的 PR#20.

setter

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;
};
复制代码

上述代码与 getter 几乎一模一样,不过使用的是 __defineSetter__,它可以在已存在的对象上添加可读属性,其中第一个参数为属性名,第二个参数为函数,参数为传入的值:

const obj = {};
obj.__defineSetter__('name', function(value) {
   this._name = value;
});
​
obj.name = 'elvin';
console.log(obj.name, obj._name);
// undefined 'elvin'
复制代码

同样地,虽然 __defineSetter__ 曾被广泛使用,但是已不被推荐,建议通过 Object.defineProperty 实现同样功能,或者通过 set 操作符实现类似功能:

const obj = {};
Object.defineProperty(obj, 'name', {
 set(value) {
   this._name = value;
 }
});
​
const dog = {
 set(value) {
   this._name = value;
 }
};
复制代码

method

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;
};
复制代码

method 的实现也十分简单,只需要注意这里 apply 函数的第一个参数是内部对象 this[target],从而确保了在执行函数 this[target][name] 时,函数体内的 this 是指向对应的内部对象。

其它 delegates 提供的函数如 fluent | access 都是类似的,就不重复说明了。

参考文章

koa 源码解析 koa-compose源码阅读

转载于:https://juejin.im/post/5ce53c956fb9a07ee062ee17

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值