ECMAScript6(18):Decorator修饰器

来源:互联网 发布:bms核心算法 编辑:程序博客网 时间:2024/06/06 19:09

相关文章

ECMAScript6 (1):块级作用域
ECMAScript6 (2):解构赋值
ECMAScript6 (3):数值类型扩展
ECMAScript6 (4):字符串类型扩展
ECMAScript6 (5):函数的扩展
ECMAScript6 (6):数组的扩展
ECMAScript6 (7):二进制数组
ECMAScript6 (8):对象的扩展
ECMAScript6 (9):正则表达式的扩展
ECMAScript6 (10):Symbol基本类型
ECMAScript6 (11):Set 与 Map
ECMAScript6 (12):Proxy 和 Reflect
ECMAScript6 (13):Generator 函数
ECMAScript6 (14):iterator 迭代器
ECMAScript6 (15):Promise 对象
ECMAScript6 (16):异步编程
ECMAScript6 (17):Class类

修饰器

修饰器是 ES7 提出的一个提案,用来修改类的行为。目前需要 babel 才可以使用。它最大的特点是:可以在编译期运行代码!其本质也就是在编译器执行的函数。其执行格式如下:

@decorator    //decorator 是修饰器名,即函数名class A{}//相当于class A{}A = decorator(A) || A;

修饰器函数接受3个参数,依次是目标函数、属性名(可忽略)、该属性的描述对象(可忽略)。

function test(target){  target.isTestable = true;               //利用修饰器给类添加静态属性  target.prototype.isTestable = true;     //利用修饰器给类添加动态属性}@testclass A{}console.log(A.isTestable);       //trueconsole.log(new A().isTestable);   //true

例如之前的 mixin 可以用修饰器实现一个简单的版本:

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};}

使用修饰器应该注意:虽然类本质是个函数,但修饰器不能用于函数,因为函数具有声明提升。

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 是一个计数器}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,表示该方法不久后将被废除,接受一个可选的参数作为警告内容, 接受第二个参数(对象)表示更多信息
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{}
原创粉丝点击