Decorators

An ES2016 decorator is an expression which returns function and can take a target, name and property descriptor as arguments.

ES2016 Decorators work on property descriptors and classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Cat {
meow() {
return `${this.name} says Meow!`;
}
}

等同于

Object.defineProperty(Cat.prototype, 'meow', {
value: specifiedFunction,
enumerable: false,
configurable: true,
writable: true
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}

class Cat {
@readonly
meow() {
return `${this.name} says Meow!`;
}
}

执行过程
let descriptor = {
value: specifiedFunction,
enumerable: false,
configurable: true,
writable: true
};
// The decorator has the same signature as `Object.defineProperty`,
// and has an opportunity to intercede before the relevant `defineProperty` actually occurs
descriptor = readonly(Cat.prototype, 'meow', descriptor) || descriptor;
Object.defineProperty(C at.prototype, 'meow', descriptor);
1
2
3
4
5
6
7
8
function superhero(target) {
target.isSuperhero = true;
target.power = 'flight';
}

@superhero
class MySuperHero {}
console.log(MySuperHero.isSuperhero); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function superhero(isSuperhero) {
return function(target) {
target.isSuperhero = isSuperhero;
target.power = 'flight';
}
}

@superhero(true)
class MySuperHero {}
console.log(MySuperHero.isSuperhero); // true

@superhero(false)
class MySuperHero {}
console.log(MySuperHero.isSuperhero); // false
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
function mixin(behaviour, sharedBehaviour = {}) {
const instanceKeys = Reflect.ownKeys(behaviour);
const sharedKeys = Reflect.ownKeys(sharedBehaviour);
const typeTag = Symbol("isa");
function _mixin(clazz) {
for (const property of instanceKeys) {
Object.defineProperty(clazz.prototype, property, {
value: behaviour[property]
});
}
Object.defineProperty(clazz.prototype, typeTag, { value: true });
return clazz;
}
for (const property of sharedKeys) {
Object.defineProperty(_mixin, property, {
value: sharedBehaviour[property],
enumerable: sharedBehaviour.propertyIsEnumerable(property)
});
}
Object.defineProperty(_mixin, Symbol.hasInstance, {
value: i => !!i[typeTag]
});
return _mixin;
}

const SuperPowers = mixin({
addPower(name) {
this.powers().push(name);
return this;
},
powers() {
return this.powersProcessed || (this.powersProcessed = []);
}
});

const UtilityBelt = mixin({
addToBelt(name) {
this.utilities().push(name);
return this;
},
utilities() {
return this.utilityItems || (this.utilityItems = []);
}
});

@SuperPowers
@UtilityBelt
class ComicBookCharacter {
constructor(first, last) {
this.firstName = first;
this.lastName = last;
}
realName() {
return this.firstName + " " + this.lastName;
}
}

const batman = new ComicBookCharacter("bruce", "Wayne");
console.log(batman.realName()); // bruce Wayne

batman.addToBelt("batarang").addToBelt("cape");
console.log(batman.utilities()); // ["batarang", "cape"]

batman.addPower("detective").addPower("voice sounds like Gollum has asthma");
console.log(batman.powers()); // ["detective", "voice sounds like Gollum has asthma"]
Functional Mixin
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
class Todo {
constructor (name) {
this.name = name || 'Untitled';
this.done = false;
}
// class method are not enumerable by defalt
do () {
this.done = true;
return this;
}
undo () {
this.done = false;
return this;
}
}

// // object mixin
// // Object.assign only assigns enumerable properties
// const Coloured = {
// setColourRGB ({r, g, b}) {
// this.colourCode = {r, g, b};
// return this;
// },
// getColourRGB () {
// return this.colourCode;
// }
// };
// Object.assign(Todo.prototype, Coloured);
// const todo = new Todo('test');
// todo.setColourRGB({r: 1, g: 2, b: 3})

/*
数据描述符默认值
{
configurable: false,
enumerable: false,
value: undefined,
writable: false
}
存取描述符默认值
{
get: undefined,
set: undefined,
value: false,
writable: false
}
*/

// functional mixin
const shared = Symbol("shared");
function FunctionalMixin (behaviour) {
// the methods defined in a mixin are enumerable by default
const instanceKeys = Reflect.ownKeys(behaviour)
.filter(key => key !== shared);

const sharedBehaviour = behaviour[shared] || {};
const sharedKeys = Reflect.ownKeys(sharedBehaviour);

const typeTag = Symbol("isA");

function mixin (target) {
for (let property of instanceKeys)
Object.defineProperty(target, property, { value: behaviour[property] });
target[typeTag] = true;
return target;
}

for (let property of sharedKeys)
Object.defineProperty(mixin, property, {
value: sharedBehaviour[property],
enumerable: sharedBehaviour.propertyIsEnumerable(property)
});

// for instanceof check
Object.defineProperty(mixin, Symbol.hasInstance, {value: (instance) => !!instance[typeTag]});

return mixin;
}
FunctionalMixin.shared = shared;


const Coloured = FunctionalMixin({
setColourRGB ({r, g, b}) {
this.colourCode = {r, g, b};
return this;
},
getColourRGB () {
return this.colourCode;
},
[FunctionalMixin.shared]: {
RED: { r: 255, g: 0, b: 0 },
GREEN: { r: 0, g: 255, b: 0 },
BLUE: { r: 0, g: 0, b: 255 },
}
});
Coloured(Todo.prototype)
const urgent = new Todo("finish blog post");
urgent.setColourRGB(Coloured.RED);
urgent.getColourRGB() //=> {"r":255,"g":0,"b":0}
urgent instanceof Todo // true
urgent instanceof Coloured //=> true
let obj = {};
obj instanceof Coloured //=> false
wx.request代理
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
38
// request abort 未测试通过
// 全局监控
let globalRequestPending = [];
const originRequest = wx.request;
Object.defineProperty(wx, 'request', {
configurable: true,
enumerable: true,
writable: true,
value: function () {
const config = arguments[0] || {};
const url = config.url;

// 同样的请求,前一个没有返回时,后续不继续请求
if (globalRequestPending.find((element) => {
return element == url ? true : false
})) {
//console.log("**拦截请求**", url);
return;
}

globalRequestPending.push(url);

const originComplete = config.complete;
config.complete = (res) => {
if (res.errMsg == "request:ok") {
// 请求完成
globalRequestPending = globalRequestPending.filter((element, index)=> {
return element == url ? false : true;
})
}
originComplete.apply(this, res);
}

//console.log("**放行请求**", url);
return originRequest.apply(this, arguments);

}
});
Reference
  1. https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.x5c2ndtx0
  2. http://raganwald.com/2015/06/17/functional-mixins.html
  3. http://raganwald.com/2015/06/26/decorators-in-es7.html