ES7 decorator 探析

起因

一直享受着 Anuglar 和 Nest 的紅利,上來就是 @Component(...) 或者 @Controller(...),自己卻沒有實際的探究過背後的原理。於是今天想好好總結一下,沉澱沉澱。

前置條件(es5 原理)

之前看過紅寶書,第六章提到過,js 對象的屬性有幾個特性:

  1. [[configurable]] 是否可配置
  2. [[enumerable]] 是否可枚舉
  3. [[writeble]] 是否可修改值
  4. [[value]] 寫入的值是啥

四個配置項都爲 boolean 類型。
這四個配置聯合起來有一個名字,叫做對象屬性的描述符(descriptor)
其中,writeble 和 value 還有另外一個名字, settergetter 訪問器)。
上代碼:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章