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
,这种新属性会非常低效。
这些变更对于 Web 平台的一致性、性能和标准化非常重要,但可能给开发者带来一些问题。如果您之前依赖 Chrome 和 WebKit 之间的旧版兼容性,我们建议您查看一下自己的网站,并在下方查看更改摘要。
变更摘要
现在,对 DOM 对象实例使用 hasOwnProperty
将返回 false
有时,开发者会使用 hasOwnProperty
来检查对象中是否存在某个属性。根据规范,此函数不再有效,因为 DOM 属性现在是原型链的一部分,而 hasOwnProperty
仅检查当前对象以查看其上是否已定义。
在 Chrome 42 之前(且包含 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
继承自 HTMLElement
,后者定义了 isContentEditable
属性。
> 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 雷达上提交问题,并参考 https://bugs.webkit.org/show_bug.cgi?id=49739
我通常有兴趣遵循此更改
- 原始错误在 2010 年发布:https://bugs.chromium.org/p/chromium/issues/detail?id=43394 - 注意:大部分问题已得到修正。
- 提交的代码审核