Chrome 團隊最近宣布我們將將 DOM 屬性移至原型鏈。本次異動在 Chrome 43 執行 - (自 2015 年 4 月中旬起實施的 Beta 版測試) 讓 Chrome 更貼近 Web IDL 規格和其他瀏覽器的實作方式,例如 IE 和 Firefox。編輯:說明:舊版 WebKit 型瀏覽器目前與規格不相容,但 Safari 現已與此規格相容。
新行為在許多方面都展現正面價值。簡要說明如下:
- 透過符合規格要求提升網路相容性 (目前 IE 和 Firefox 已經這樣做)。
- 能讓您以一致且有效率的方式,為每個 DOM 物件建立 getter/setter。
- 提高 DOM 程式設計的可入侵性。舉例來說,您可以實作 polyfill,以快速模擬某些瀏覽器和 JavaScript 程式庫中缺少的功能,而這些程式庫會覆寫預設的 DOM 屬性行為。
舉例來說,假設的 W3C 規格包含一些名為 isSuperContentEditable
的新功能,但 Chrome 瀏覽器並未實作此功能,但您可以使用「polyfill」或使用程式庫模擬此功能。身為程式庫開發人員,您自然會想要使用 prototype
來建立有效的 polyfill:
Object.defineProperty(HTMLDivElement.prototype, "isSuperContentEditable", {
get: function() { return true; },
set: function() { /* some logic to set it up */ },
});
在這項變更之前,為了與 Chrome 中其他 DOM 屬性一致,你得在每個執行個體中建立新的屬性,因此網頁上每個 HTMLDivElement
的效率都會很低。
這些變更對於網路平台的一致性、效能和標準化非常重要,但也可能會造成開發人員了一些問題。如果您因為 Chrome 和 WebKit 的舊版相容性而仰賴這項行為,建議您檢查自己的網站,並查看下方的異動摘要。
異動內容摘要
現在對 DOM 物件執行個體使用 hasOwnProperty
會傳回 false
開發人員有時會使用 hasOwnProperty
來檢查物件上是否有屬性。這個做法無法根據規格運作,因為 DOM 屬性現在是原型鏈鏈的一部分,而 hasOwnProperty
只會檢查目前的物件,確認是否已在其中定義。
在 Chrome 42 (含) 以下版本中,以下函式會傳回 true
。
> div = document.createElement("div");
> div.hasOwnProperty("isContentEditable");
true
Chrome 43 以上版本會傳回 false
。
> div = document.createElement("div");
> div.hasOwnProperty("isContentEditable");
false
現在,如果您要檢查元素是否提供 isContentEditable
,即可檢查 HTMLElement 物件的原型。舉例來說,HTMLDivElement
繼承自定義 isContentEditable
屬性的 HTMLElement
。
> HTMLElement.prototype.hasOwnProperty("isContentEditable");
true
您並未受鎖定,無法使用「hasOwnProperty
」。建議您使用更簡單的 in
運算元,因為這麼做會在整個原型鏈結上檢查屬性。
if("isContentEditable" in div) {
// We have support!!
}
DOM 物件執行個體上的 Object.getOwnPropertyDescriptor 不會再傳回 Attributes 屬性描述元
如果您的網站需要取得 DOM 物件上屬性的屬性描述元,現在就需要遵循原型鏈。
如果想取得 Chrome 42 以下版本的屬性說明,請按下列步驟操作:
> Object.getOwnPropertyDescriptor(div, "isContentEditable");
Object {value: "", writable: true, enumerable: true, configurable: true}
Chrome 43 以上版本將傳回 undefined
。
> Object.getOwnPropertyDescriptor(div, "isContentEditable");
undefined
也就是說,如要取得 isContentEditable
屬性的屬性描述元,您必須遵循原型鏈結,如下所示:
> Object.getOwnPropertyDescriptor(HTMLElement.prototype, "isContentEditable");
Object {get: function, set: function, enumerable: false, configurable: false}
JSON.stringify 不會再序列化 DOM 屬性
JSON.stringify
不會序列化原型上的 DOM 屬性。舉例來說,如果您嘗試將物件 (例如推播通知的 PushSubscription) 進行序列化,你的網站就會受到影響。
Chrome 42 以下版本適用的功能如下:
> JSON.stringify(subscription);
{
"endpoint": "https://something",
"subscriptionId": "SomeID"
}
Chrome 43 以上版本不會序列化在原型上定義的屬性,而且您將傳回空白物件。
> JSON.stringify(subscription);
{}
您必須提供自己的序列化方法,例如:
function stringifyDOMObject(object)
{
function deepCopy(src) {
if (typeof src != "object")
return src;
var dst = Array.isArray(src) ? [] : {};
for (var property in src) {
dst[property] = deepCopy(src[property]);
}
return dst;
}
return JSON.stringify(deepCopy(object));
}
var s = stringifyDOMObject(domObject);
在嚴格模式中寫入唯讀屬性將擲回錯誤
使用嚴格模式寫入唯讀屬性時,應擲回例外狀況。例如,請採取下列做法:
function foo() {
"use strict";
var d = document.createElement("div");
console.log(d.isContentEditable);
d.isContentEditable = 1;
console.log(d.isContentEditable);
}
Chrome 42 以下版本執行函式時,系統會持續以無訊息的方式執行函式,不過 isContentEditable
並未變更。
// Chrome 42 and earlier behavior
> foo();
false // isContentEditable
false // isContentEditable (after writing to read-only property)
現在,Chrome 43 以上版本會擲回例外狀況。
// Chrome 43 and onwards behavior
> foo();
false
Uncaught TypeError: Cannot set property isContentEditable of #<HTMLElement> which has only a getter
我遇到問題,該怎麼辦?
請按照指南進行,或是在下方留言,與我們交流。
我發現某個網站有問題,該怎麼辦?
好問題大多數網站問題都是因為網站選擇使用 getOwnProperty
方法偵測屬性狀態開發人員可以採取下列幾項行動:
- 透過 Chrome 的 Issue Tracker 回報受影響網站的問題
- 提出 WebKit radar 問題,並附上 https://bugs.webkit.org/show_bug.cgi?id=49739
我有意因應這項異動
- 2010 的原始錯誤:https://bugs.chromium.org/p/chromium/issues/detail?id=43394 - 注意:這個問題的大部分工作。
- 修訂程式碼審查