ECMAScript6(18):Decorator修飾器

修飾器

修飾器是 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};
}

使用修飾器應該注意:雖然類本質是個函數,但修飾器不能用於函數,由於函數具備聲明提高。函數

core-decroators.js

這是個三方模塊,使用import {function Namelist} from 'core-decroators';引入。它提供了幾個常見的修飾器:post

  • @autobind

是對象中的 this 始終綁定原始對象:this

class Person{
  @autobind
  whoami(){
    return this;
  }
}
let person = new Person();
let getPerson = person.getPerson;

getPerson() === person;    //true
  • @readonly

使得屬性方法只讀url

class Person{
  @readonly
  id = gen();     //gen 是一個計數器
}
var p = new Person()
p.id = 123;   //Cannot assign to read only property 'id' of [object Object]
  • @override

檢查子類方法是否正確的覆蓋了父類的同名方法,若是不正確會報錯prototype

class Person{
  work(){console.log("I am working");}
}
class Coder extends Person{
  @override
  work(){console.log("I am coding");}   //若是不正確會在這裏報錯
}
  • @deprecate(也做: @deprecated)

在控制檯顯示一條 warning,表示該方法不久後將被廢除,接受一個可選的參數做爲警告內容, 接受第二個參數(對象)表示更多信息

class Person{
  @deprecate
  facepalm(){}

  @deprecate('We stopped facepalming')
  facepalmHard(){}

  @deprecate('We stopped facepalming', {url:'http://balabala.com'})
  facepalmHarder(){}
}
  • @suppressWarnings

抑制 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{}
相關文章
相關標籤/搜索