装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression 这种形式,expression 求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
(一)使用
注意:装饰器是一项实验性特性,在未来的版本中可能会发生改变。
在 TypeScript 中装饰器还属于实验性语法,你必须在命令行或 tsconfig.json 里启用 experimentalDecorators 编译器选项:
- 命令行:
tsc --target ES5 --experimentalDecorators
- tsconfig.json:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
1、装饰类
针对类的修饰,会接受一个参数即类对象本身,下文通过对类添加静态属性实现。
@testable
class Demo{}
function testable(target) {
target.isTestable=true
}
console.log(Demo.isTestable) // true
2、 装饰类方法
针对类方法修饰,函数会接收3个参数:
- 1、target:当前对象的原型
- 2、key:当前方法名
- 3、descriptor:方法的属性描述符
// descriptor对象原来的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
// target:在方法中的target指向类的prototype
function readonly(target,key,descriptor){
descriptor.writable=false
return descriptor
}
class Demo {
@readonly
print(){console.log(`a:${this.a}`)}
}
3、 装饰类属性
针对类属性装饰器,函数会接收2个参数:
- 1、target:当前对象的原型
- 2、key:当前属性名
function set(value: string) {
return function (target: any, propertyName: string) {
target[propertyName] = value;
}
}
class Demo {
@set("hello world") greeting: string;
}
console.log(new Demo().greeting);// hello world
(二)装饰器的执行顺序
多个修饰器的执行顺序是由外向内进入;再由内向外执行。
class Demo {
@log(1)
@log(2)
method(){}
}
function log(id){
console.log('id is ',id)
return (target,property,descriptor)=>console.log('executed',id)
}
// 控制台输出
// id is 1
// id is 2
// executed 2
// executed 1
(三)实现几个简单的装饰器
本文用到的个小方法
// 返回空对象
export const noop = () => { };
/**
* 判断对象类型
* @param [obj: any] 参数对象
* @returns String
*/
export function isType(obj) {
return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
}
1、装饰器实现异常捕获
/**
* 用于捕获Error
* @tips 请注意,参数params中的回调函数不能使用箭头函数,否则将会引起上下文变动
* @param [Object | Function] params 接收异常捕获回调,非必选参数
* @param [params.callback] callback 默认接收Function类型的参数,或者接收 Object 对象中的callback方法
* @returns descriptor
*/
export const DRCatchError = (params: any = noop) => (target, key, descriptor) => {
const original = descriptor.value;
let errorHandler: any = null;
if (isType(params) === 'function') {
errorHandler = params;
} else if (isType(params) === 'object') {
errorHandler = params.callback;
}
descriptor.value = async function () {
try {
await original.apply(this, arguments);
} catch (error) {
let resp = (error && error.response) || {};
let errorData = resp.data || error.message;
if (typeof errorHandler === 'function') {
errorHandler.call(this, errorData);
} else {
console.error(error);
}
}
};
return descriptor;
};
2、装饰器实现防抖
/**
* 用于操作方法防抖
* @param [wait: number] 延时ms
* @param [immediate: boolean] 立即执行
* @returns descriptor
*/
export const DRDebounce = (wait: number, immediate: boolean = false) => (target, key, descriptor) => {
let timeout: any;
const original = descriptor.value;
descriptor.value = function () {
let later = () => {
timeout = null;
if (!immediate) {
original.apply(this, arguments);
}
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
original.apply(this, arguments);
}
};
return descriptor;
};
3、装饰器实现节流
/**
* 用于操作函数节流
* @param [delay: number] 延时ms
*/
export const DRThrottle = (delay: number) => (target, key, descriptor) => {
let last: any;
let deferTimer: any;
const original = descriptor.value;
descriptor.value = function() {
let now = +new Date();
if (last && now < last + delay) {
clearTimeout(deferTimer);
deferTimer = setTimeout(() => {
last = now;
original.apply(this, arguments);
}, delay);
}else {
last = now;
original.apply(this, arguments);
}
};
return descriptor;
};
(四)参考链接
(五)其他链接
微信公众号:前端杂论