一、什么是js修饰器
修饰器(Decorator)是ES7的一个提案,它是用于对类和类的方法进行处理的函数
二、使用
1、对类进行修饰
@testable
class MyTestableClass {
// ...省略
}
function testable(target) {
target.isTestable = true
}
MyTestableClass.isTestable // true
如上代码所示,testable
就是一个修饰器,它对MyTestableClass
进行了修改,添加了一个isTestable
属性。
并且可以看到,修饰器testable
的第一个参数就是所修饰的类,如果需要传入更多参数,则需要在修饰器内再写一层函数。
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
上面代码中的修饰器testable
就可以接收其他参数了,而被修饰的类则作为它返回出来的函数的参数。
注意,修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数
以上是对类添加一个静态属性的例子,如果需要添加实例属性(即对类的实例也都进行修改),则需要对类的prototype
对象进行操作。
function testable(target) {
target.prototype.isTestable = true;
}
@testable
class MyTestableClass {}
let obj = new MyTestableClass();
obj.isTestable // true
这样对类的prototype
对象进行了修改,使得类的实例也都继承了修改的属性。
2、对类的方法进行修改
当修饰器对类的方法进行修改时,可以接收三个参数,分别是类的原型对象(即类的prototype
对象),修饰器的本意是要“修饰”类的实例,但是这个时候实例还没生成,所以只能去修饰原型(这不同于类的修饰,那种情况时target参数指的是类本身);第二个参数是所要修饰的属性名(即被修改的方法名),第三个参数是该属性的描述对象(即被修改的方法本身)。
class User {
@readonly
getName() {
return 'Hello World'
}
}
// readonly修饰函数,对方法进行只读操作
function readonly(target, name, descriptor) {
descriptor.writable = false //将方法的可写属性设为false,即不可写
return descriptor
}
let u = new User()
// 尝试修改函数,因为已经设置为不可写,所以在控制台会报错
u.getName = () => {
return 'I will override'
}
如果要修改方法的内容,则可以用value
属性
descriptor.value = 'function (){ console.log('Hello decorator') }'
这样执行方法时,会输出Hello decorator
,而非'I will override
。
如果同一个方法有多个修饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。(即写在后面的先执行)
3、为什么修饰器不能用于函数?
修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。类是不会提升的,所以就没有这方面的问题。
4、mixin
在修饰器的基础上,可以实现Mixin模式。所谓Mixin模式,就是对象继承的一种替代方案,中文译为“混入”(mix in),意为在一个对象之中混入另外一个对象的方法。
一个通用minxin脚本
export function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list);
};
}
使用:
import { mixins } from './mixins';
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // "foo"
不过,上面的方法会改写MyClass类的prototype对象,如果不喜欢这一点,也可以通过类的继承实现 Mixin。