我最近在與客戶合作的專案中,需要充分利用的 ECMAScript 5,在此我遇到一個非常有趣的問題。 這個問題源自於一個非常常見的模式: mixin , 也就是在 JavaScript 中把一個物件的屬性或是方法 mixin 到另一個。
大多數 mixin 的功能看起來像這樣:
mixin(object, {
console.log(this.name);
}
object.sayName(); // outputs "Nicholas"
在此範例中,object 物件接收了屬性 name 和方法 sayName()。 這在 ECMAScript 3 中運作良好,但在 ECMAScript 5 上卻沒那麼樂觀。
複製程式碼
// to be filled in later
var name;
mixin(object, {
return name;
}
// let's just say this is later
name = "Nicholas";
console.log(object.name); // undefined
這個例子看起來有點做作,但它準確的描述這個問題。 進行 mixin 的屬性使用了 ECMAScript 5 的新特性:一個 getter 屬性存取器。 getter 引用一個未初始化的局部變數 name,因此這個屬性未定義 undefined。
後來,name 被指派了一個值,以便讓存取器 getter 可以傳回有效的值。 不幸的是,object.name(被 mixin 的屬性)始終傳回 undefined。
這是怎麼回事?
我們仔細分析 mixin() 函數。 事實上,在循環語句中,並沒有把屬性從一個物件重新賦值給另一個物件。 它實際上是創建一個同名的屬性,並將 supplier 物件的存取器方法 getter 的回傳值賦值給了它。 (譯註:目標物件得到的不是 getter 這個方法,而是得到了 getter 方法的回傳值。@justjavac)
複製程式碼🎜>
屬性 receiver.name 被創建,並且被賦值為 supplier.name 的值。 當然,supplier.name 有一個 getter 方法用來傳回本地變數 name 的值。 此時,name 的值為 undefined,所以 receiver.name 儲存的是 值。 並沒有為 receiver.name 建立一個 getter 方法,因此它的值永遠不會改變。
要解決這個問題,你需要使用屬性描述符(譯註:descriptor)將屬性從一個物件 mixin 到另一個物件。 一個純粹的 ECMAScript 5 版本的 mixin() 應該是這樣寫:
function mixin(receiver, suppl.
Object.keys(supplier).forEach(function(value, property) {
Object.defineProperty(receiver, property, Object.getOwnPropertyDescriptor(supa);
在這個新版本函數中,Object.keys() 用來取得一個數組,包含了 supplier 物件的所有枚舉屬性。 然後,foreach() 方法用來遍歷這些屬性。 呼叫 Object.getOwnPropertyDescriptor() 方法取得 supplier 物件的每個屬性描述符(descriptor)。
由於描述子(descriptor)包含了所有的屬性訊息,包括 getter 和 setter 方法, 這個描述子(descriptor)可以直接傳遞給 Object.defineProperty() ,用來在 receiver 物件上建立相同的屬性。 使用這個新版本的 mixin() ,可以解決前面遇到的問題,從而得到你所期望的結果。 getter 方法被正確地從 supplier 傳遞到了 receiver。
當然,如果你還是需要支援舊的瀏覽器,那麼你就需要一個函數,回落的 ECMAScript 3: