最近看koa的源码,看到了了一个TJ大神写的包delegates ,实现了委托模式/代理模式 。
当前包的版本为1.0.0,代码就100多行,周下载量一千两百多万。。果然是巨佬。
基本结构 目录结构很简单,主要文件只有一个index.js和test文件夹下的单元测试。
index.js文件的内容:
Delegator :构造方法
Delegator.auto :自动委托所有可以委托的属性
Delegator.prototype.method :委托一个方法
Delegator.prototype.getter :委托属性的getter 方法
Delegator.prototype.setter :委托属性的setter 方法
Delegator.prototype.access : 同时委托属性的getter 和setter 方法
Delegator.prototype.fluent :委托属性,实现jQuery风格,不传参数获取属性值,传参修改属性值。
ps: 在koa源码中,使用的也是delegates的1.0.0的包,但是index.js中没有找到auto这个方法。
举例和实现 构造方法 1 2 3 4 5 6 7 8 9 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 = []; }
这是一个构造函数,if (!(this instanceof Delegator)) return new Delegator(proto, target);
这一行的目的是,可以使用Delegator(proto,target)来实例化一个对象。
method 源码没什么难度,核心代码:
1 2 3 proto[name] = function ( ) { return this [target][name].apply (this [target], arguments ); };
举个列子:
1 2 3 4 5 6 7 8 9 10 const delegates = require ('delegates' )var obj = {}obj.request = { foo : (foo ) => { return foo; } } delegates (obj, 'request' ).method ('foo' )console .log (obj.foo ('aaa' )); console .log (obj.request .foo ('bbb' ));
getter 举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const delegates = require ('delegates' );var obj = {}obj.request = { foo : (foo ) => { return foo; }, phone : 'iphone11' , get getter1 () { return 'hello' ; } } Object .defineProperty (obj.request , 'getter' , { get : function ( ) { return this .phone ; }, enumerable : true }) delegates (obj, 'request' ).getter ('getter' );delegates (obj, 'request' ).getter ('getter1' );console .log (obj.getter ); console .log (obj.getter1 );
从上面的代码例子中可以知道,get getter(){}还是Object.defineProperty效果是一样的。
核心代码:
1 2 3 proto.__defineGetter__ (name, function ( ) { return this [target][name]; });
defineGetter 方法可以将一个函数绑定在当前对象的指定属性上,当那个属性的值被读取时,你所绑定的函数就会被调用。
但是在MDN web docs文档中说它是一个非标准的方法,未来可能会停止支持。。。
所以这样改一下也没什么问题吧:
1 2 3 4 5 Object .defineProperty (proto, name, { get : function ( ) { return this [target][name] } })
setter 举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const delegates = require ('delegates' );var obj = {}obj.request = { foo : (foo ) => { return foo; }, phone : 'iphone11' } Object .defineProperty (obj.request , 'setter' , { set : function (value ) { this .phone = value; }, enumerable : true }) delegates (obj, 'request' ).setter ('setter' );obj.setter = 'iphone12' ; console .log (obj.request .phone );
核心代码:
1 2 3 proto.__defineSetter__ (name, function (val ) { return this [target][name] = val; });
同理也可以改为:
1 2 3 4 5 Object .defineProperty (proto, name, { get : function (val ) { return this [target][name] = val; } })
access 核心代码:
1 return this .getter (name).setter (name);
没什么好说的,就是同时代理了getter和setter
fluent 核心代码也比较简单:
1 2 3 4 5 6 7 8 proto[name] = function (val ) { if ('undefined' != typeof val) { this [target][name] = val; return this ; } else { return this [target][name]; } };
根据代码可以看出来,fluent代理的属性,传参时为获取值,不传参时为设置值。
auto 不过神奇的是,我直接下载的源码有这个方法并且有对应的测试用例,但是npm下载的包中确没有auto这个方法。
新建一个Delegator对象,获取被委托对象的所有属性。
循环遍历这些属性。
获取这个属性的详细描述(Object.getOwnPropertyDescriptor)。
如果有get方法,委托getter。
如果有set方法,委托setter。
如果有value属性:
如果value是Function类型,委托method。
如果不是委托getter。
如果这个属性是可写的writable = true,委托setter。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Delegator .auto = function (proto, targetProto, targetProp ) { var delegator = Delegator (proto, targetProp); var properties = Object .getOwnPropertyNames (targetProto); for (var i = 0 ; i < properties.length ; i++) { var property = properties[i]; var descriptor = Object .getOwnPropertyDescriptor (targetProto, property); if (descriptor.get ) { delegator.getter (property); } if (descriptor.set ) { delegator.setter (property); } if (descriptor.hasOwnProperty ('value' )) { var value = descriptor.value ; if (value instanceof Function ) { delegator.method (property); } else { delegator.getter (property); } if (descriptor.writable ) { delegator.setter (property); } } } };