前言
通过前两篇文章《优秀装饰器源码学习(一):time》,《优秀装饰器源码学习(二)》学习了
@time
,@deprecate
, @readonly
, @enumerable
, @nonconfigurable
等基础的装饰器
本文和大家一起学习几个稍微复杂一点的装饰器:
@mixin
:混入方法到类中@lazyInitialize
:在使用的时候才初始化目标属性
@mixin
混入对象中的方法到类中
使用示例
通过@mixin(obj1,obj2,obj3,..)
就能将对象中的属性,挂载到目标类的原型上
就行Vue中通过mixin混入一些公共的方法
import { mixin } from '../index' const obj1 = { logA() { console.log(this.a); } } const obj2 = { printKeys() { console.log(Object.keys(this)); } } @mixin(obj1, obj2) class Test { public a = 1 } const t: any = new Test() t.logA() // 1 t.printKeys() // ['a']
函数结构
传入参数:
- objs:rest参数,支持传入多个对象进行混入的操作
function handleClass(target, mixins) { if (!mixins.length) { throw new SyntaxError(`@mixin() class ${target.name} requires at least one mixin as an argument`); } } export default function mixin(...objs) { return function (target) { return handleClass(target, objs) } }
实现原理
- 类装饰器第一个参数
target
标识装饰的类 - target.prototype标识类的原型
- 遍历传入的对象,通过Object上的
getOwnPropertyNames
与getOwnPropertyDescriptor
方法分别获取目标对象的自有属性(不包括Symbol属性)与指定属性的描述符 - 在通过
Object.defineProperty
实现在Class.prototype
上进行拓展
handleClass的完整实现
/** * 获取对象上的每个属性的描述符 */ function getOwnPropertyDescriptors(obj) { const descs = {}; Object.getOwnPropertyNames(obj).forEach(key => { descs[key] = Object.getOwnPropertyDescriptor(obj, key) }) return descs; } function handleClass(target, mixins) { if (!mixins.length) { throw new SyntaxError(`@mixin() class ${target.name} requires at least one mixin as an argument`); } for (let i = 0; i < mixins.length; i++) { const descs = getOwnPropertyDescriptors(mixins[i]) const keys = Object.getOwnPropertyNames(mixins[i]) for (let j = 0; j < keys.length; j++) { const key = keys[j]; if (!target.prototype.hasOwnProperty(key)) { Object.defineProperty(target.prototype, key, descs[key]) } } } }
lazyInitialize
懒加载指定属性,即在使用的时候才初始化目标属性
使用示例
将需要懒执行的逻辑放入到@lazyInitialize
之中
import { lazyInitialize } from ".."; function getMaxArray(str=''){ console.log(str,'init huge array'); return new Array(100) } class Test{ @lazyInitialize(()=>getMaxArray('a')) public a public b = getMaxArray('b') } const t = new Test() const k = new Test() k.a
运行结果
b init huge array b init huge array a init huge array
实现原理
- 使用闭包存储值初始化的函数
- 修改属性的get行为,当调用get的时候再执行初始化逻辑
- 将初始化后的内容使用中间变量暂存
- 再次get调用属性的时候,直接返回暂存的内容
函数实现
function createDefaultSetter(key) { return function set(newValue) { Object.defineProperty(this, key, { configurable: true, writable: true, enumerable: true, value: newValue }); return newValue; }; } export default function lazyInitialize(initializer): any { let t return function (target, key) { return { get() { t = t === undefined? initializer.call(this) : t return t; }, set: createDefaultSetter(key) } } }
未完待续
下一篇将学习:
@debounce
:防抖@throttle
:节流