JS的 decorator
已經(jīng)到了Stage 2 Draft
的階段阿弃。并且在 Babel 7
中得到支持制圈。
修飾器其實(shí)在其他的語言中已經(jīng)得到很完善的實(shí)現(xiàn)了贸伐,比如 Python
克伊。修飾器主要的功能是將輔助性的功能和核心功能分開。例如衙传,核心功能可以是主營業(yè)務(wù)指蚁,輔助功能可以是身份驗(yàn)證,信息緩存或者日記功能颈娜。這些輔助功能大部分是可以共享的剑逃,并且和主要功能沒有什么聯(lián)系,所以解耦和重用是修飾器很重要的功能官辽。
相關(guān)信息
不同類型修飾器的應(yīng)用和分析
類修飾器
使用場景:為類添加靜態(tài)屬性或者方法蛹磺。
修飾器的參數(shù):
- target: 類本身
例子:
function language(value) {
return function(target) {
target.language = value;
}
}
@language('English')
class Country {}
console.log(Country.language);
# output: English
實(shí)例方法修飾器
應(yīng)用場景:輸出日志,性能計(jì)算同仆,權(quán)限驗(yàn)證萤捆。
修飾器的參數(shù):
- target:類的prototype。
- name:要修飾的方法名稱俗批。
- descriptor:該方法的修飾器俗或。
例子:
function beforeFunc(target, name, descriptor) {
const func = descriptor.value;
const newFunc = function() {
console.log(`Before calling: ${name}`);
const result = func.apply(this, arguments);
return result;
}
return {
...descriptor,
value: newFunc
}
}
function afterFunc(target, name, descriptor) {
const func = descriptor.value;
const newFunc = function() {
const result = func.apply(this, arguments);
console.log(`After calling: ${name}`);
return result;
}
return {
...descriptor,
value: newFunc
}
}
class Medium {
constructor() {
this.base = 10;
}
@afterFunc
@beforeFunc
add(a, b){
console.log(`Calculating...`);
return a + b + this.base;
}
}
// console.log(Country.language);
const m = new Medium();
console.log('Sum: ', m.add(1, 4));
# Output:
# Before calling: add
# Calculating...
# After calling: add
# Sum: 15
實(shí)例屬性修飾器
使用場景:修改property的屬性(writable,enumerable岁忘,configurable等)辛慰,監(jiān)聽屬性的變化(watcher)。
修飾器的參數(shù):
- target:類的prototype臭觉。
- name:要修飾的方法名稱昆雀。
- descriptor:該方法的修飾器。
Note: 使用數(shù)據(jù)描述符和存儲描述符定義的屬性蝠筑,它們的descriptor是不一樣的。
例子:
- 存儲描述符: 給屬性添加監(jiān)聽器
function observable(target, name, descriptor) {
const setter = descriptor.set;
return {
...descriptor,
get: function() {
console.log(`Before getting '${name}'. '${name}' is ${this.base}`);
return this.base;
},
set: function(value) {
console.log(`Before setting '${name}'. '${name}' is ${this.base}`);
setter.call(this, value);
return true;
}
};
}
class Medium {
@observable
get count () {
return this.base;
};
set count (value) {
this.base = value;
};
constructor() {
this.base = 10;
}
}
const m = new Medium();
m.count += 20;
console.log('Count: ', m.count);
# Output:
# Before getting 'count'. 'count' is 10
# Before setting 'count'. 'count' is 10
# Before getting 'count'. 'count' is 30
# Count: 30
- 數(shù)據(jù)描述符: 將屬性設(shè)置為只讀
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Medium {
@readonly
age = 18;
}
const m = new Medium();
m.age = 28;
# throw an error:
# TypeError: Cannot assign to read only property 'age' of object '#<Medium>'
Desugar
說到底JS中的修飾器只是一種語法糖揩懒,最終還是由解釋器翻譯成一般的JS語法什乙。JS的修飾器主要依賴于 Object.defineProperty
功能,當(dāng)解釋器遇到了 @decorator
的語法時(shí)已球,就會調(diào)用這個(gè)修飾器函數(shù)對屬性(方法)的描述符(descriptor)進(jìn)行操作臣镣,然后將修飾過的 descriptor
重新定義屬性辅愿。
具體的轉(zhuǎn)義可以查看:https://github.com/wycats/javascript-decorators#desugaring