delegates简单实现委托模式
用法
delegates 基本用法就是将内部对象的变量或者函数绑定在暴露在外层的变量上,直接通过 delegates
方法进行如下委托,基本的委托方式包含:
- getter:外部对象可以直接访问内部对象的值
- setter:外部对象可以直接修改内部对象的值
- access:包含 getter 与 setter 的功能
- method:外部对象可以直接调用内部对象的函数
const delegates = require('./index');
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 delegates = require('./index');
const petShop = {
dog: {
name: '旺财',
},
}
delegates(petShop, 'dog')
.fluent('name');
// 不传参数,获取内部属性
console.log(petShop.name());
// 传参数,修改内部属性
// 还可以链式调用
console.log(
petShop.name('二哈')
.name('蠢二哈')
.name();
);
源码学习
初始化
// 源码 7 - 1
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 = [];
}
getter
// 源码 7-2
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,不过因为 TJ 已经离开了 Node.js 社区,所以估计也不会更新这个仓库了。
setter
// 源码 7-3
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
// 源码 7-4
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 中,其核心就在于 context
对象,许多读写操作都是基于它进行,例如:
- ctx.header 获取请求头
- ctx.method 获取请求方法
- ctx.url 获取请求 URL
这些对请求参数的获取都得益于 koa 中 context.request
的许多属性都被委托在了 context
上:
// Koa 源码 lib/context.js
delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.access('path')
.access('url')
.getter('headers')
.getter('ip');
// ...
又例如:
- ctx.body 设置响应体
- ctx.status 设置响应状态码
- ctx.redirect() 请求重定向
这些对响应参数的设置都得益于 koa 中 context.response
的许多属性和方法都被委托在了 context
上:
// Koa 源码 lib/context.js
delegate(proto, 'response')
.method('redirect')
.method('vary')
.access('status')
.access('body')
.getter('headerSent')
.getter('writable');
// ...
版权声明
本站部分原创文章,部分文章整理自网络。如有转载的文章侵犯了您的版权,请联系站长删除处理。如果您有优质文章,欢迎发稿给我们!联系站长:
愿本站的内容能为您的学习、工作带来绵薄之力。
评论