修飾器是 ES7 提出的一個提案,用來修改類的行爲。目前須要 babel 纔可使用。它最大的特色是:能夠在編譯期運行代碼!其本質也就是在編譯器執行的函數。其執行格式以下:git
@decorator //decorator 是修飾器名,即函數名 class A{} //至關於 class A{} A = decorator(A) || A;
修飾器函數接受3個參數,依次是目標函數、屬性名(可忽略)、該屬性的描述對象(可忽略)。github
function test(target){ target.isTestable = true; //利用修飾器給類添加靜態屬性 target.prototype.isTestable = true; //利用修飾器給類添加動態屬性 } @test class A{} console.log(A.isTestable); //true console.log(new A().isTestable); //true
例如以前的 mixin 能夠用修飾器實現一個簡單的版本:babel
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個修飾器:ide
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}; }
使用修飾器應該注意:雖然類本質是個函數,但修飾器不能用於函數,由於函數具備聲明提高。函數
這是個三方模塊,使用import {function Namelist} from 'core-decroators';
引入。它提供了幾個常見的修飾器:post
是對象中的 this 始終綁定原始對象:this
class Person{ @autobind whoami(){ return this; } } let person = new Person(); let getPerson = person.getPerson; getPerson() === person; //true
使得屬性方法只讀url
class Person{ @readonly id = gen(); //gen 是一個計數器 } var p = new Person() p.id = 123; //Cannot assign to read only property 'id' of [object Object]
檢查子類方法是否正確的覆蓋了父類的同名方法,若是不正確會報錯prototype
class Person{ work(){console.log("I am working");} } class Coder extends Person{ @override work(){console.log("I am coding");} //若是不正確會在這裏報錯 }
在控制檯顯示一條 warning,表示該方法不久後將被廢除,接受一個可選的參數做爲警告內容, 接受第二個參數(對象)表示更多信息
class Person{ @deprecate facepalm(){} @deprecate('We stopped facepalming') facepalmHard(){} @deprecate('We stopped facepalming', {url:'http://balabala.com'}) facepalmHarder(){} }
抑制 deprecate 修飾器致使調用 console.warn(), 但異步代碼發出的除外。
class Person{ @deprecate facepalm(){} @supressWarnings facepalmWithoutWarning(){ this.facepalm(); } } let p = new Person(); p.facepalm(); //控制檯顯示警告 p.facepalmWithoutWarning(); //沒有警告
此外還有一些庫提供一些其餘功能,好比 Postal.js(Github)中的 @publish
, 能夠在函數調用時發佈一個事件:
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{}