起因
一直享受着 Anuglar 和 Nest 的紅利,上來就是 @Component(...)
或者 @Controller(...)
,自己卻沒有實際的探究過背後的原理。於是今天想好好總結一下,沉澱沉澱。
前置條件(es5 原理)
之前看過紅寶書,第六章提到過,js 對象的屬性有幾個特性:
- [[configurable]] 是否可配置
- [[enumerable]] 是否可枚舉
- [[writeble]] 是否可修改值
- [[value]] 寫入的值是啥
四個配置項都爲 boolean 類型。
這四個配置聯合起來有一個名字,叫做對象屬性的描述符(descriptor)
其中,writeble 和 value 還有另外一個名字, setter
和 getter
訪問器)。
上代碼:
const obj = { };
Object.defineProperty(obj,'a', {
value: 1,
writeble: false,
});
console.log(obj); // {a: 1}
console.log(obj.a) // 1
obj.a = 3; // 修改 a 屬性的值
console.log(obj.a) // 1
/**====================另一種寫法====================*/
const d = {};
Object.defineProperty(d , 'name' {
get: function() {return 1},
set: function(value) {return false}
});
console.log(d) // {} 注意!!!!這裏跟 writeble 和 value 不太一樣,這裏打印出來的對象,是沒有顯示 name 屬性的!!!但是訪問可以訪問出來
d.name; // 1
d.name = 3; // 嘗試修改 name 屬性
d.name; // 1
我們發現,配置了可寫入項爲 false 時,我們就無法去修改對象屬性的值了,有點像凍結的意思。剛好,JS 有個 Object.freeze()
, 來看一下
const c = {name: 1};
Object.freeze(c);
c.name = 3;
console.log(c) // {a: 1}
發現和我們自己去配置 writeble: false
效果相同。不信?來驗證一下:
Object.getOwnPropertyDescriptor(c);
// 返回:
{
name: {
configurable: false
enumerable: true
value: 1
writable: false
}
}
ES6 還要這麼寫嗎?
不用。直接用裝飾器 decorator
來寫。
第一種,裝飾屬性
裝飾器會在 Object.defineProperty
之前執行,也就是攔截默認的訪問修飾符。
舉個例子:
// CSDN markdown 編輯器 爲什麼不支持 typescript 高亮?無語...
function nameEqual3(object, key, descriptor: PropertyDescriptor) {
descriptor.value = 3;
descriptor.writable = false;
}
class Person {
@nameEqual3
name() { }
}
const p = new Person();
console.log(p.name); // 3
可見其效果。等等,爲什麼裝飾器的入參沒有了捏?
因爲,在 TypeScript 中,默認的參數被直接傳入被編譯後的 runtime 的 object.property
上
第二種,直接裝飾 class,
作用: 給類增加動態屬性,該動態屬性並不會被繼承,只能作爲 被裝飾類 的 靜態屬性
function addFlag(object) {
object.flag = true;
}
@addFlag
class Foo(){}
Foo.flag // true
// 來個實例
const f1 = new Foo();
f1.flag // undefined
第三種,工廠模式在裝飾器上的應用
@isDog(false)
class Man(){}
@isDog(true)
class Dog(){}
function isDog(flag: boolean) {
return function(target) {
target.isDog = flag;
}
}
console.log(Man.isDog); // false
console.log(Dog.isDog); // true