优秀装饰器源码学习(三)
前言
通过前两篇文章《优秀装饰器源码学习(一):time》,《优秀装饰器源码学习(二)》学习了
@time
,@deprecate
, @readonly
, @enumerable
, @nonconfigurable
等基础的装饰器
本文和大家一起学习几个稍微复杂一点的装饰器:
@mixin
:混入方法到类中@lazyInitialize
:在使用的时候才初始化目标属性
@mixin
混入对象中的方法到类中
使用示例
通过@mixin(obj1,obj2,obj3,..)
就能将对象中的属性,挂载到目标类的原型上
就行Vue中通过mixin混入一些公共的方法
ts
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参数,支持传入多个对象进行混入的操作
ts
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的完整实现
ts
/**
* 获取对象上的每个属性的描述符
*/
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
之中
ts
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
运行结果
sh
b init huge array
b init huge array
a init huge array
实现原理
- 使用闭包存储值初始化的函数
- 修改属性的get行为,当调用get的时候再执行初始化逻辑
- 将初始化后的内容使用中间变量暂存
- 再次get调用属性的时候,直接返回暂存的内容
函数实现
ts
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
:节流