類的修飾
許多面向對象的語言都有裝飾器(Decorator)函數,用來修改類的行為。目前,有一個提案將這項功能,引入了 ECMAScript。
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
上面代碼中,@testable
就是一個裝飾器。它修改了MyTestableClass
這個類的行為,為它加上了靜態屬性isTestable
。testable
函數的參數target
是MyTestableClass
類本身。
基本上,裝飾器的行為就是下面這樣。
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
也就是說,裝飾器是一個對類進行處理的函數。裝飾器函數的第一個參數,就是所要修飾的目標類。
如果覺得一個參數不夠用,可以在裝飾器外面再封裝一層函數。
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
上面代碼中,裝飾器函數testable是在目標類的prototype對象上添加屬性,因此就可以在實例上調用。
方法的修飾
裝飾器不僅可以修飾類,還可以修飾類的屬性。
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
上面代碼中,裝飾器readonly用來修飾“類”的name方法。
此時,裝飾器函數一共可以接受三個參數,第一個參數是所要修飾的目標對象,即類的實例(這不同于類的修飾,那種情況時target參數指的是類本身);第二個參數是所要修飾的屬性名,第三個參數是該屬性的描述對象。
function readonly(target, name, descriptor){
// descriptor對象原來的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable = false;
return descriptor;
}
readonly(Person.prototype, 'name', descriptor);
// 類似于
Object.defineProperty(Person.prototype, 'name', descriptor);
上面代碼說明,裝飾器(readonly)會修改屬性的描述對象(descriptor),然后被修改的描述對象再用來定義屬性。
使用裝飾器的注意點
- 修飾器只能用于類和類的方法,不能用于函數,因為存在函數提升