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
来说都非常低效。
这些更改对于 Web 平台的一致性、性能和标准化至关重要,但可能会给开发者带来一些问题。如果您之前之所以依赖此行为,是因为 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 年的原始 bug:https://bugs.chromium.org/p/chromium/issues/detail?id=43394 - 注意:已完成大部分工作。
- 提交的代码审核