修饰器模式
什么是修饰器模式? 修饰器是es7引入的,用通俗易懂的话解释就是:执行一个方法,在代码编译时执行,把修饰器下满的class或者属性作为参数传入,并返回一个新的方法,新的方法再在运行时执行。
上栗子:
@addStaticProp
class Test {
}
function addStaticProp(target) {
target._name = '新增的属性';
}
console.log(Test._name); // 此处会输出 '新增的属性'
@addStaticProp的作用是直接修改了它下方的class的属性,添加了一个新的静态属性,就类似一个加工厂,把需要的加工的物品丢进去,而我们不用管这个物品内部有什么,我们只是在外部给该物品添加多一些内容。此时再回顾上面说的那句话就很容易理解了。
其实在其他语言早已经有类似的方法了,例如Java的注解,就是跟我们的修饰器一毛一样的。
class使用修饰器:
自由添加属性和值:
function addCustomProp (key, value) {
return function (target) {
target[key] = value
}
}
@addCustomProp('isShow', true)
class Test {
}
@addCustomProp('name', 'yixin')
class Test {
}
console.log(Test.isShow); // true
console.log(Test.name); // yixin
给类自由添加实例方法:
function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list)
}
}
const say = () => {
console.log('hello world!')
}
@minxins(say)
class TestClass {}
let p1 = new TestClass();
p1.say(); // hello world
此处的修饰器直接给class的原型上面添加了方法,所以实例出来的对象都会有该方法。
方法使用修饰器
修饰器作用在方法上面会接受到三个参数,参数1:要修饰的目标对象,参数2:要修饰的属性名,参数3:该属性的描述对象。 描述对象内容如下:
configurable: 是否可删除enumerable: 是否可枚举(遍历)value: 属性值writable: 是否可修改
来个下酒菜,把实例属性修改为只读:
function readonly(target, name, descriptor) {
descriptor.writable = false
return descriptor;
}
class Test{
@readonly
public name = 'yixin';
}
let p1 = new Test();
p1.name = '改名了';
console.log(p1.name); // yixin 修改失败
再来个最常用的日记修饰器log
function log(target, name, descriptor) {
let oFn = descriptor.value;
descriptor.value = function() {
console.log(`方法${name}正在执行,参数为:`, arguments);
return oFn.apply(null, arguments)
}
return descriptor;
}
class Test{
@log
public add(a, b) {
return a + b
}
}
(new Test()).add(1,3); // 方法$add正在执行,参数为:`, [1,3]
多个修饰器使用:
方法可以迭代使用多个修饰器,不过由于修饰器是编译时执行,运行时执行修饰器编译后的代码,所以当一个方法有多个修饰器的时候,那么该方法会从外到内进入修饰器,然后由内向外执行。
function consoleLog(value) {
return function (target, name, descriptor) {
console.log(value);
}
}
class Test {
@consoleLog(1);
@consoleLog(2);
say() {}
}
(new Test()).say(); // 即使最上面的修饰器先编译,但是结果依旧是先输出2,再输出1。
防抖修饰器
防抖是我们开发中使用最多的方法
function debounce(wait) {
return function(target, name, descriptor) {
const oFn = descriptor.value
let timer = null
descriptor.value = function() {
clearTimeout(timer);
timer = setTimeout(() => {
oFn.apply(_this, arguments)
}, wait)
}
}
}
class Test {
@debounce(500)
search() {
console.log('查询操作');
}
}
let p1 = new Test();
p1.search();
p1.search();
p1.search();
p1.search();
节流修饰器
节流也是我们开发中使用最多的方法
function throttle(wait) {
return function(target, name, descriptor) {
const oFn = descriptor.value
let timer = null
descriptor.value = function() {
if (timer) return
fn.apply(_this, arguments)
timer = setTimeout(() => {
canRun = null
}, wait)
}
}
}
class Test {
@throttle(500)
search() {
console.log('查询操作');
}
}
let p1 = new Test();
p1.search();
p1.search();
p1.search();
p1.search();