详解ES7 Decorator 入门解析
Decorator提供了一种独特的抽象逻辑,可在原有代码基础上,零侵入添加新功能特性。商业代码总是多种交织并存的,在日常开发中,除了实现业务功能之外,我们还需要考虑诸如:异常处理、性能分析、日志等额外的需求。未经设计的的开发方法会倾向于将各种需求耦合组成一个功能模块,比如:
classMath{
staticadd(num1,num2){
try{
console.time('somelabel');
log('logforsomething');
constresult=num1+num2;
console.timeEnd('somelabel');
returnresult;
}catch(e){
error('somethinghadbroken');
}
}
}
上述简单的两数相加功能,在添加各类需求之后,已经变的面目全非。Decorator语法通过描述,可将功能特性叠加到原有功能中:
classMath{
@log
@error
@time
staticadd(num1,num2){
returnnum1+num2;
}
}
Decorator是什么
Decorator就是一个的包裹函数,运行时在编译阶段调用该函数,修改目标对象的行为、属性。我们先来看一个简单实例:
constlog=(target,prop)=>console.log(`Wrapfunction:'${prop}'`);
consttec={
@log
say(){
console.log('helloworld')
}
}
//=>Wrapfunction'say'
Decorator函数签名如下:
//@paramtarget作用对象
//@paramprop作用的属性名
//@paramdescriptor属性描述符
//@returndescriptor属性描述符
functiondecorator(target,prop,descriptor){}
参数详解:
- target:作用的对象,有如下情况:
- 作用于class时,target为该class函数
- 作用于class中的函数、属性时,target为该class的prototype对象
- 作用于对象字面量中的函数、属性时,target为该对象
- prop:描述的属性名,若decorator作用于class时,该参数为空
- descriptor:属性原本的描述符,该描述符可通过Object.getOwnPropertyDescriptor()获取,若decorator作用于class时,该参数为空
- decorator函数支持返回描述符或undefined,当返回值为描述符时,运行时会调用Object.defineProperty()修改原有属性。
Decorator的ES5实现
理解Decorator机制,最佳方式是使用ES5实现该过程。
class装饰器机制比较简单,仅做一层包装,伪代码:
//调用实例
@log
classPerson{}
//实现代码
constPerson=log(Person);
属性装饰器机制则比较复杂,babel就此提供了一个参考范例:
//decorator处理
function_applyDecoratedDescriptor(target,property,decorators,descriptor,context){
vardesc={};
Object['ke'+'ys'](descriptor).forEach(function(key){
desc[key]=descriptor[key];
});
desc.enumerable=!!desc.enumerable;
desc.configurable=!!desc.configurable;
if('value'indesc||desc.initializer){
desc.writable=true;
}
desc=decorators.slice().reverse().reduce(function(desc,decorator){
returndecorator(target,property,desc)||desc;
},desc);
if(context&&desc.initializer!==void0){
desc.value=desc.initializer?desc.initializer.call(context):void0;
desc.initializer=undefined;
}
if(desc.initializer===void0){
Object['define'+'Property'](target,property,desc);
desc=null;
}
returndesc;
}
//调用实例
classPerson{
@log
say(){}
}
//实现代码
_applyDecoratedDescriptor(
Person.prototype,
'say',
[log],
Object.getOwnPropertyDescriptor(Person.prototype,'say'),
Person.prototype)
)
用例
Decorator主要应用于如下几类对象:
- class
- class中,除构造函数外的方法
- class中的属性
- 对象字面量中的函数
- 对象字面量中的属性
//类
@log
classPerson{
//函数
@log
say(){}
//属性
@log
name='tec';
}
//同样适用于对象字面量的方法、属性
consttec={
@log
name:'tec',
@log
walk(){}
};
Decorator实践
在JS中,Decorator是一个新概念,对于多数没有接触过诸如python、C#的开发者而言,很难理解实际应用场景。幸运的是github已经有人封装了常用Decorator。笔者分析该库,总结如下几种定义模式:
通过descriptor的value值修改:
functiondecorate(target,key,descriptor){
constfn=descriptor.value;
return{
...descriptor,
value(){
returnfn.apply(this,arguments);
}
}
}
通过descriptor的get、set函数修改:
functiondecorate(target,key,descriptor){
letvalue=descriptor.value;
return{
...descriptor,
get(){
returnvalue;
}
set(v){
value=v;
}
}
}
通过descriptor的writable、enumerable等属性修改:
functionreadonly(target,key,descriptor){
return{
...descriptor,
writable:false
}
}
针对class,返回包裹函数
functionlog(target){
letinitTimes=0;
return(...arg)=>{
console.log(++initTimes);
target.call(this,...arg);
};
}
在实际开发中,还需要注意以下事项:
- Decorator的目标是在原有功能基础上,添加功能,切忌覆盖原有功能
- Decorator不是管道模式,decorator之间不存在交互,所以必须注意保持decorator独立性、透明性
- Decorator更适用于非业务功能需求
- 确定decorator的用途后,切记执行判断参数类型
- decorator针对每个装饰目标,仅执行一次
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。