Atrybuty DOM są teraz w łańcuchu prototypu

Zespół Chrome niedawno ogłosił, że przenosi właściwości DOM do łańcucha prototypów. Ta zmiana, wprowadzona w Chrome 43 (wersja beta z połowy kwietnia 2015 r.), sprawia, że Chrome jest bardziej zgodny z specyfikacją Web IDL i implementacjami w innych przeglądarkach, takich jak IE i Firefox. Edytuj: wyjaśnienie  Starsze przeglądarki oparte na WebKit nie są obecnie zgodne ze specyfikacją, ale Safari już jest.

Nowe zachowanie ma wiele zalet. Oto one:

  • Poprawia zgodność w internecie (IE i Firefox już to robią) dzięki zgodności ze specyfikacją.
  • Umożliwia spójne i wydajne tworzenie metod get i set w przypadku każdego obiektu DOM.
  • Zwiększa podatność na włamanie kodu programowania DOM. Na przykład możesz zaimplementować elementy polyfill, które umożliwiają skuteczne emulowanie funkcji niedostępnych w niektórych przeglądarkach i bibliotekach JavaScript, które zastępują domyślne zachowania atrybutów DOM.

Na przykład hipotetyczna specyfikacja W3C zawiera nową funkcję o nazwie isSuperContentEditable, której przeglądarka Chrome nie obsługuje, ale można ją zaimplementować za pomocą biblioteki. Jako twórca biblioteki możesz użyć funkcji prototype w taki sposób, aby utworzyć wydajne rozwiązanie polyfill:

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

Przed wprowadzeniem tej zmiany – ze względu na spójność z innymi właściwościami DOM w Chrome – należałoby utworzyć nową właściwość w każdym wystąpieniu, co w przypadku każdego HTMLDivElement na stronie byłoby bardzo niewydajne.

Te zmiany są ważne dla spójności, wydajności i ujednolicania platformy internetowej, ale mogą powodować pewne problemy dla deweloperów. Jeśli polegasz na tym zachowaniu ze względu na zgodność starszych wersji Chrome i WebKit, sprawdź swoją witrynę i przeczytaj podsumowanie zmian poniżej.

Podsumowanie zmian

Użycie funkcji hasOwnProperty na przykładzie obiektu DOM spowoduje teraz zwrócenie wartości false.

Czasami deweloperzy używają hasOwnProperty, aby sprawdzić, czy w obiekcie występuje dana właściwość. Zgodnie ze specyfikacją nie będzie to już działać, ponieważ atrybuty DOM są teraz częścią łańcucha prototypów, a hasOwnProperty sprawdza tylko bieżące obiekty, aby sprawdzić, czy są w nich zdefiniowane.

Przed wersją Chrome 42 i w jej lidze zwracano wartość true.

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

true

Od wersji Chrome 43 zwraca on wartość false.

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

false

Oznacza to, że jeśli chcesz sprawdzić, czy element isContentEditable jest dostępny, musisz sprawdzić prototyp obiektu HTMLElement. Na przykład klasa HTMLDivElement dziedziczy z klasy HTMLElement, która definiuje właściwość isContentEditable.

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

true

Nie musisz używać aplikacji hasOwnProperty. Zalecamy użycie znacznie prostszego operandu in, ponieważ sprawdza on właściwość w całym łańcuchu prototypów.

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

Obiekt.getOwnPropertyDescriptor w przypadku obiektu DOM nie będzie już zwracać opisu właściwości dla atrybutów

Jeśli Twoja witryna musi uzyskać opis właściwości dla atrybutu obiektu DOM, musisz teraz śledzić łańcuch prototypów.

Aby uzyskać opis obiektu w Chrome w wersji 42 lub starszej, wykonaj te czynności:

> Object.getOwnPropertyDescriptor(div, "isContentEditable");

Object {value: "", writable: true, enumerable: true, configurable: true}

W tym scenariuszu Chrome 43 i nowsze wersje zwracają undefined.

> Object.getOwnPropertyDescriptor(div, "isContentEditable");

undefined

Oznacza to, że aby uzyskać opis właściwości isContentEditable, musisz teraz postępować zgodnie z łańcuchem prototypów w taki sposób:

> Object.getOwnPropertyDescriptor(HTMLElement.prototype, "isContentEditable");

Object {get: function, set: function, enumerable: false, configurable: false}

Funkcja JSON.stringify nie będzie już serializować atrybutów DOM.

JSON.stringify nie serializuje właściwości DOM, które znajdują się w prototypie. Może to na przykład wpływać na Twoją witrynę, jeśli próbujesz serializować obiekt, np. PushSubscription powiadomienia push.

W Chrome 42 i starszych działały te opcje:

> JSON.stringify(subscription);

{
    "endpoint": "https://something",
    "subscriptionId": "SomeID"
}

Od wersji 43 Chrome nie będzie serializować właściwości zdefiniowanych w prototypie, a zamiast tego zwróci pusty obiekt.

> JSON.stringify(subscription);

{}

Musisz podać własną metodę serializacji. Możesz na przykład wykonać te czynności:

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);

Zapisywanie właściwości tylko do odczytu w rygorystycznym trybie spowoduje błąd

Zapisywanie danych w właściwościach tylko do odczytu powinno wywołać wyjątek, gdy używasz trybu rygorystycznego. Na przykład:

function foo() {
    "use strict";
    var d = document.createElement("div");
    console.log(d.isContentEditable);
    d.isContentEditable = 1;
    console.log(d.isContentEditable);
}

W Chrome 42 i starszych funkcja ta była nadal wykonywana, ale isContentEditable nie ulegał zmianie.

// Chrome 42 and earlier behavior
> foo();

false // isContentEditable
false // isContentEditable (after writing to read-only property)

Od wersji 43 Chrome będzie wyrzucać wyjątek.

// Chrome 43 and onwards behavior
> foo();

false
Uncaught TypeError: Cannot set property isContentEditable of #<HTMLElement> which has only a getter

Mam problem. Co mam zrobić?

Postępuj zgodnie z tymi wskazówkami lub zostaw komentarz poniżej.

Co mam zrobić, jeśli widzę problem z witryną?

Świetne pytanie. Większość problemów z witrynami wynika z tego, że witryna używa metody getOwnProperty do wykrywania obecności atrybutów. Dzieje się tak głównie wtedy, gdy właściciel witryny kieruje reklamy tylko do starszych przeglądarek WebKit. Deweloperzy mogą zrobić kilka rzeczy:

  • Prześlij zgłoszenie dotyczące problemu z witryną w naszym systemie śledzenia problemów (Chrome)
  • Zgłoś problem w systemie śledzenia błędów WebKit i odwołaj się do https://bugs.webkit.org/show_bug.cgi?id=49739

Interesuje mnie ta zmiana