Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

原型链上的DOM Attributes #12

Open
zmmbreeze opened this issue Aug 31, 2016 · 0 comments
Open

原型链上的DOM Attributes #12

zmmbreeze opened this issue Aug 31, 2016 · 0 comments

Comments

@zmmbreeze
Copy link
Owner

zmmbreeze commented Aug 31, 2016

以前的旧文章,转移到这里

本文翻译自html5rocks的DOM Attributes now on the prototype chain

Chrome开发小组最近发表声明他们正在將DOM properties移动到原型链中。这个更新将会在Chrome 43(2015年4月发布beta版本)中实现。这可以让Chrome与Web IDL标准以及其他浏览器(IE和Firfox)保持一致。注:旧的基于Webkit的浏览器与标准不兼容但是safari已经与标准兼容了。

注意:本篇文章中Attribute与Property没有区别,并可以互换。ECMA script标准定义了Properties包括Attributes。一个JS property等于一个WebIDL Attribute。本文里面的Attribute并不是HTML Attribute(例如image元素上class)。

这项更新有很多好处:

  • 因为遵循了规范,所以跨浏览器的兼容性更好(IE和Firefox早就与规范保持一致了)
  • 让开发者一致且高效地创建DOM对象上的getter/setter
  • 提高DOM编程的灵活性。例如:你可以更加方便地实现polyfill方案。你可以模拟在某些浏览器中缺失的属性。或者你可以重写DOM属性的行为

例如,假设有一个W3C规范规定的新属性叫做isSuperContentEditable,并且Chrome还没有实现它。但是我们可以实现一个polyfill去模拟这个特性。作为一个库的开发者,很自然地你会想到修改prototype来实现这个属性,这样更加高效。

    Object.defineProperty(HTMLDivElement.prototype, "isSuperContentEditable", {
        get: function() { return true; },
        set: function() { /* some logic to set it up */ },
    });

在这次更新之前,为了在Chrome中与其他DOM属性保持一致,一定要为每个实例增加新的isSuperContentEditable属性。为页面上的每一个HTMLDivElement实例创建新属性是非常低效的。

这些更新对Web平台的一致性、性能和规范化都很重要。当然这也会带来一些不兼容的问题。如果你以前依赖过Chrome或Webkit的这个特性,强烈建议检查下自己的站点并阅读下面的更新总结。

更新总结

在DOM实例上调用hasOwnProperties现在会返回false

有时开发者会调用hasOwnProperties方法来检查属性是否某个对象上。以后这将不再起作用。因为DOM属性都移动到了原型链中,而hasOwnProperties方法不会检查原型链上是否有这个属性。

在Chrome 42及以前版本中,如下代码的执行结果为true

> div = document.createElement("div");
> div.hasOwnProperty("isContentEditable");
true

在Chrome 43及之后的版本中华,将会返回false

> div = document.createElement("div");
> div.hasOwnProperty("isContentEditable");
false

这意味着如果你想要检查DOM上的isContentEditable属性是否可用,那么需要检查HTMLObject的prototype。例如:HTMLDivElement继承自HTMLElement ,而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不会序列化prototype上的DOM属性。例如,如果你尝试序列化Push Notication的PushSubscription对象,那么你的代码会受到影响。

Chrome 42及以前的版本下,如下代码可以正常工作:

> JSON.stringify(subscription);
{
    "endpoint": "https://something",
    "subscriptionId": "SomeID"
}

Chrome 43及以后的版本中将不会序列化DOM属性,因为他们定义在prototype上。最后只会返回一个空对象。

> 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及以前的版本中修改只读DOM属性不会抛出异常但也不会生效。

// 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方法来做属性支持与否的检测,来兼容旧的浏览器。那么那个站点的开发者可以做如下事情:

我就是对这个改动感兴趣

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant