Атрибуты DOM теперь в цепочке прототипов

Команда Chrome недавно объявила, что мы перемещаем свойства DOM в цепочку прототипов . Это изменение, реализованное в Chrome 43 (бета-версия на середину апреля 2015 г.), приводит Chrome в большее соответствие со спецификацией Web IDL и реализациями других браузеров, таких как IE и Firefox. Редактировать: уточнено Старые браузеры на базе WebKit в настоящее время несовместимы со спецификацией, однако Safari теперь совместим.

Новое поведение во многих отношениях положительно. Оно:

  • Улучшает совместимость в Интернете (IE и Firefox уже это делают) за счет соответствия спецификации.
  • Позволяет последовательно и эффективно создавать геттеры/сеттеры для каждого объекта DOM.
  • Увеличивает хакерскую способность программирования DOM. Например, это позволит вам реализовать полифиллы, которые позволят вам эффективно эмулировать функциональность, отсутствующую в некоторых браузерах, и библиотеки JavaScript, которые переопределяют поведение атрибутов DOM по умолчанию.

Например, гипотетическая спецификация W3C включает в себя некоторые новые функции, называемые isSuperContentEditable , а браузер Chrome не реализует их, но можно "полифиллить" или эмулировать эту функцию с помощью библиотеки. Как разработчик библиотеки, вы, естественно, захотите использовать prototype следующим образом для создания эффективного полифилла:

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

До этого изменения — для обеспечения согласованности с другими свойствами DOM в Chrome — вам пришлось бы создавать новое свойство для каждого экземпляра, что для каждого HTMLDivElement на странице было бы крайне неэффективно.

Эти изменения важны для согласованности, производительности и стандартизации веб-платформы, однако они могут вызвать некоторые проблемы у разработчиков. Если вы полагались на это поведение из-за устаревшей совместимости между Chrome и WebKit, мы рекомендуем вам проверить свой сайт и ознакомиться с обзором изменений ниже.

Краткое изложение изменений

Использование hasOwnProperty для экземпляра объекта DOM теперь будет возвращать 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 наследуется от HTMLElement , который определяет свойство isContentEditable .

> HTMLElement.prototype.hasOwnProperty("isContentEditable");

true

Вы не ограничены использованием hasOwnProperty . Мы рекомендуем использовать гораздо более простой операнд in , поскольку он будет проверять свойство по всей цепочке прототипов.

if("isContentEditable" in div) {
    // We have support!!
}

Object.getOwnPropertyDescriptor для экземпляра объекта DOM больше не будет возвращать дескриптор свойства для атрибутов.

Если вашему сайту необходимо получить дескриптор свойства для атрибута объекта 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 Push Notification.

В 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

Мне в целом интересно следить за этим изменением