Chrome 團隊最近宣布將 DOM 屬性移至原型鏈結。這項變更已在 Chrome 43 (2015 年 4 月中旬的 Beta 版) 中實作,讓 Chrome 更符合 Web IDL 規格和其他瀏覽器 (例如 IE 和 Firefox) 的實作方式。編輯:已澄清 舊版 WebKit 瀏覽器目前與規格不相容,但 Safari 已相容。
這項新行為在許多方面都具有正面影響。其中包括:
- 遵循規格,改善網頁的相容性 (IE 和 Firefox 已實現此目標)。
- 可讓您在每個 DOM 物件上一致且有效率地建立 getter/setter。
- 提高 DOM 程式設計的駭客攻擊可能性。舉例來說,您可以實作 polyfill,藉此有效模擬某些瀏覽器和 JavaScript 程式庫中缺少的功能,這些瀏覽器和 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 將不再傳回屬性屬性描述項
如果您的網站需要取得 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
方法進行屬性存在偵測,而這類情況通常是網站擁有者只指定舊版 WebKit 瀏覽器時才會發生。開發人員可以採取下列幾種行動:
- 在 Chrome 問題追蹤工具中,針對受影響的網站回報問題
- 在 WebKit Radar 上提出問題,並參考 https://bugs.webkit.org/show_bug.cgi?id=49739
我對這項變更的一般動向感興趣
- 2010 年原始錯誤:https://bugs.chromium.org/p/chromium/issues/detail?id=43394 - 注意:大部分工作都已完成。
- 提交的程式碼審查