润饰器
润饰器是 ES7 提出的一个提案,用来修正类的行动。现在须要 babel 才能够运用。它最大的特点是:能够在编译期运转代码!其实质也就是在编译器实行的函数。其实行花样以下:
@decorator //decorator 是润饰器名,即函数名
class A{}
//相当于
class A{}
A = decorator(A) || A;
润饰器函数接收3个参数,依次是目的函数、属性名(可疏忽)、该属性的形貌对象(可疏忽)。
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 能够用润饰器完成一个简朴的版本:
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{}