es6之修饰器

修饰器

类的修饰器

修饰器对类的行为的改变是在代码编译时候发生的,而不是在运行的时候

1
2
3
4
5
6
7
8
function testable(target){
target.isTestable = true
}

@testable
class MyTestableClass{}

console.log(MyTestableClass.isTestable)

修饰器的行为如下

1
2
3
4
5
6
@decorator
class A{}

//等同于
class A{}
A = decorator(A) || A;

如果需要多个参数,就在修饰器的外部在封装一层函数

1
2
3
4
5
6
7
8
9
function testable(isTestable){
return function (target){
target.isTestable = isTestable;
}
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable; //true

以上是为类添加静态属性,下面为类的实例添加属性,通过目标类的prototype对象操作

1
2
3
4
5
6
7
8
9
function testable(target){
target.prototype.isTestable = true;
}

@testable
class MyTestableClass {}

let obj = new MyTestableClass();
obj.isTestable;

使用mixin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//mixins.js
export function mixins(...list){
return function(target){
Object.assign(target.prototype,...list)
}
}


//main.js
import {mixins} from './mixins'

const Foo = {
foo() {console.log('foo')}
};

@mixins(Foo)
class MyClass {}

let obj = new MyClass();
obj.foo(); //'foo'

方法的修饰

1
2
3
4
5
6
class Person{
@readonly
name(){
return `${this.first} ${this.last}`
}
}

此时,修饰器接受三个参数:
1) 所要修饰的目标对象
2) 所要修饰的属性名
3) 该属性的描述对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function readonly(target,name,descriptor){
//descriptor对象原来的值如下
//{
// value:specifiedFunction,
// enumerable:false,
// configurable:true,
// writable:true
//}

descriptor.writable = false;
return descriptor
}

readonly(Person.prototype,'name',descriptor);
//类似与
Object.defineProperty(Person.prototype,'name',descriptor);

下面的@log修饰器,可以起到输出日志的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Math{
@log
add(a,b){
return a+b;
}
}

function log(target,name,descriptor){
var oldVal = descriptor.value; //先保存函数的引用

descriptor.value = function(){
console.log(`Calling "${name}" with`,arguments);
return oldVal.apply(null,arguments)
}
return descriptor;
}

const math = new Math();
math.add(1,2);

//----------------------------------
var obj = {
p(a,b){
return a+b
}

};

console.log(Object.getOwnPropertyDescriptor(obj, 'p'))
/*
Object {
"configurable": true,
"enumerable": true,
"value": [Function p],
"writable": true
}
*/

如果一个方法有多个修饰器,先从外到内进入,再由内到外执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function desc(id){
console.log('evaluated',id);
return (target,property,descriptor) => conosle.log('executed',id);
}

class Example {
@desc(1)
@desc(2)
method(){}
}

// evaluated 1
// evaluated 2
// executed 2
// executed 1

修饰器不能用于函数

因为存在函数提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var counter = 0;
var add = function(){
counter++
}
@add
function foo(){}

//由于存在变量提升,所以实际的执行如下
@add
function foo(){}

var counter;
var add;

counter =0;
add = function(){
counter++;
}

Mixin

是对象继承的一种替代,在一个对象之中混入另一个对象的方法

1
2
3
4
5
6
7
8
9
10
11
const Foo = {
foo(){
console.log('foo')
}
}

class MyClass{}

Object.assign(MyClass.prototype,Foo)
let obj = new MyClass();
obj.foo();

将mixin写成修饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//mixins.js
export function mixins(...list){
return function(target){
Object.assign(target.prototype,...list)
}
}

import {mixins} from './mixins';

const Foo = {
foo(){
console.log('foo')
}
}

@mixins(Foo)
class MyClass{}

let obj = new MyClass();
obj.foo(); //'foo'

以上方法会改写MyClassprototype对象,所以可以使用类的继承实现mixin

1
2
3
class MyClass extends MyBaseClass {
//...
}

如果我们想在MyClass中混入foo方法,可以在MyClassMyBaseClass之间插入一个混合类

1
2
3
4
5
let MyMixin = (superclass)=> class extends superclass {
foo(){
console.log('foo from Mymixin');
}
}

上面接受superclass为参数,返回一个继承superclass的子类,该子类包含一个foo方法

1
2
3
4
5
6
class MyClass extends MyMixin(MyBaseClass) {
//...
}

let c = new MyClass();
c.foo();

上面的写法有一个好处就是可以调用super,从而避免在混入过程中覆盖父类的同名函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let Mixin1 = (superclass) => class extends superclass{
foo(){
console.log('foo from Mixin1');
if(super.foo) super.foo();
}
}

let Mixin2 = (superclass) => class extends superclass{
foo(){
console.log('foo from Mixin2');
if(super.foo) super.foo();
}
}

class S {
foo() {
console.log('foo from S');
}
}

class C extends Mixin1(Mixin2(S)) {
foo() {
console.log('foo from C');
super.foo();
}
}

let c = new C();
c.foo();
//"foo from C"
//"foo from Mixin1"
//"foo from Mixin2"
//"foo from S"

使用修饰器实现自动发布事件

利用修饰器,使得对象的方法被调用的时候,自动发出一个事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import postal from 'postal/lib/postal.lodash';

export default function publish(topic,channel){
return function(target,name,descriptor){
const fn = descriptor.value;

descriptor.value = function(){
let value = fn.apply(this,arguments);
postal.channel(channel || target.channel || "/").publish(topic, value);
}
}
};

//使用
import publish from 'publish';

class FooComponent {
@publish('foo.some.message','component')
someMethod(){
return {
my:'data'
}
}

@publish("foo.some.other")
anotherMethod() {
// ...
}
}

let foo = new FooComponent();

foo.someMethod() // 在"component"频道发布"foo.some.message"事件,附带的数据是{ my: "data" }
foo.anotherMethod() // 在"/"频道发布"foo.some.other"事件,不附带数据

文章目录
  1. 1. 类的修饰器
  2. 2. 方法的修饰
  3. 3. 修饰器不能用于函数
  4. 4. Mixin
  5. 5. 使用修饰器实现自动发布事件