修飾器
修飾器是 ES7 提出的一個提案,用來修改類的行為。目前需要 babel 才可以使用子姜。它最大的特點是:可以在編譯期運行代碼巨柒!其本質也就是在編譯器執(zhí)行的函數(shù)。其執(zhí)行格式如下:
@decorator //decorator 是修飾器名,即函數(shù)名
class A{}
//相當于
class A{}
A = decorator(A) || A;
修飾器函數(shù)接受3個參數(shù),依次是目標函數(shù)、屬性名(可忽略)哗伯、該屬性的描述對象(可忽略)。
function test(target){
target.isTestable = true; //利用修飾器給類添加靜態(tài)屬性
target.prototype.isTestable = true; //利用修飾器給類添加動態(tài)屬性
}
@test
class A{}
console.log(A.isTestable); //true
console.log(new A().isTestable); //true
例如之前的 mixin 可以用修飾器實現(xiàn)一個簡單的版本:
function mixins(...list){
return function(target){
Object.assign(target.prototype, ...list);
}
}
var Foo = {
foo(){console.log("foo");}
};
@mixins(Foo)
class Cla{}
let obj = new Cla();
obj.foo(); //"foo"
修飾器不僅僅可以修飾類篷角,還可以修飾類的屬性和方法:
function readonly(target, name, descriptor){
descriptor.writable = false;
return descriptor;
}
class Person{
constructor(name, age, tel){
this.name = name;
this.id = id;
}
@readonly
id(){return this.id};
}
當然也可以同時調用2個修飾器:
function readonly(target, name, descriptor){
descriptor.writable = false;
return descriptor;
}
function nonenumerable(target, name, descriptor){
descriptor.enumerable = false;
return descriptor;
}
class Person{
constructor(name, age, tel){
this.name = name;
this.id = id;
}
@readonly
@nonenumerable
id(){return this.id};
}
使用修飾器應該注意:雖然類本質是個函數(shù)焊刹,但修飾器不能用于函數(shù),因為函數(shù)具有聲明提升恳蹲。
core-decroators.js
這是個三方模塊虐块,使用import {function Namelist} from 'core-decroators';
引入。它提供了幾個常見的修飾器:
- @autobind
是對象中的 this 始終綁定原始對象:
class Person{
@autobind
whoami(){
return this;
}
}
let person = new Person();
let getPerson = person.getPerson;
getPerson() === person; //true
- @readonly
使得屬性方法只讀
class Person{
@readonly
id = gen(); //gen 是一個計數(shù)器
}
var p = new Person()
p.id = 123; //Cannot assign to read only property 'id' of [object Object]
- @override
檢查子類方法是否正確的覆蓋了父類的同名方法嘉蕾,如果不正確會報錯
class Person{
work(){console.log("I am working");}
}
class Coder extends Person{
@override
work(){console.log("I am coding");} //如果不正確會在這里報錯
}
- @deprecate(也作: @deprecated)
在控制臺顯示一條 warning贺奠,表示該方法不久后將被廢除,接受一個可選的參數(shù)作為警告內(nèi)容, 接受第二個參數(shù)(對象)表示更多信息
class Person{
@deprecate
facepalm(){}
@deprecate('We stopped facepalming')
facepalmHard(){}
@deprecate('We stopped facepalming', {url:'http://balabala.com'})
facepalmHarder(){}
}
- @suppressWarnings
抑制 deprecate 修飾器導致調用 console.warn(), 但異步代碼發(fā)出的除外错忱。
class Person{
@deprecate
facepalm(){}
@supressWarnings
facepalmWithoutWarning(){
this.facepalm();
}
}
let p = new Person();
p.facepalm(); //控制臺顯示警告
p.facepalmWithoutWarning(); //沒有警告
其它第三方修飾器
此外還有一些庫提供一些其他功能儡率,比如 Postal.js(Github)中的 @publish
, 可以在函數(shù)調用時發(fā)布一個事件:
import publish from "../to/decorators/publish";
class FooComponent{
@publish("foo.some.message", "component")
someMethod(){}
@publish("foo.some.other", "")
anotherMethod(){}
}
再比如 Trait(Github), 和 mixin 功能類似,提供了更強大的功能:防止同名沖突以清,排除混入某些方法儿普,為混入方法起別名等
import {traits} from 'traits-decorator'
class TFoo{
foo(){console.log("foo1")}
}
class TBar{
bar(){console.log("bar")}
foo(){console.log("foo2")}
}
@traits(TFoo, TBar) //會報錯,因為這兩個類中有同名方法
class MyClass{}
let obj = new MyClass();
//如果沒有第八行的同名方法掷倔,輸出如下
obj.foo(); //"foo1"
obj.bar(); //"bar"
但是我們可以修改上面第11行排除這個 foo眉孩,讓它可以被覆蓋:
@traits(TFoo, TBar::excludes('foo'))
class MyClass{}
也可重命名同名方法:
@traits(TFoo, TBar::alias(foo:'aliasFoo'))
class MyClass{}
當然綁定運算符可以鏈式調用:
//假設還有個同名的 baz 方法
@traits(TFoo, TBar::excludes('foo')::alias(baz:'aliasBaz'))
class MyClass{}
//另一種寫法
@traits(TFoo, TBar::as({excludes: ['foo'], alias: {baz:'aliasBaz'}}))
class MyClass{}