Los atributos del DOM ahora están en la cadena de prototipos

Recientemente, el equipo de Chrome anunció que trasladaremos las propiedades del DOM a la cadena de prototipos. Este cambio, implementado en Chrome 43 (versión beta a partir de mediados de abril de 2015), permite que Chrome cumpla con las especificaciones de Web IDL y las implementaciones de otros navegadores, como IE y Firefox. Edición: aclarado: Los navegadores anteriores basados en WebKit, actualmente no son compatibles con la especificación, pero ahora Safari.

El nuevo comportamiento es positivo en muchos sentidos. Por ejemplo:

  • Mejora la compatibilidad en la Web (IE y Firefox ya lo hacen) al cumplir con las especificaciones.
  • Te permite crear métodos get y set de manera coherente y eficiente en cada objeto DOM.
  • Aumenta la capacidad de hackeo de la programación DOM. Por ejemplo, te permitirá implementar polyfills que te permiten emular de forma eficiente la funcionalidad que falta en algunos navegadores y bibliotecas JavaScript que anulan los comportamientos predeterminados de los atributos del DOM.

Por ejemplo, una especificación W3C hipotética incluye una funcionalidad nueva llamada isSuperContentEditable y el navegador Chrome no la implementa, pero es posible "polyfill" o emular la función con una biblioteca. Como desarrollador de bibliotecas, te gustaría usar prototype de la siguiente manera para crear un polyfill eficiente:

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

Antes de este cambio, para mantener la coherencia con otras propiedades del DOM en Chrome, habrías tenido que crear la nueva propiedad en cada instancia, lo que para cada HTMLDivElement en la página sería muy ineficiente.

Estos cambios son importantes para la coherencia, el rendimiento y la estandarización de la plataforma web, pero pueden causarles algunos problemas a los desarrolladores. Si dependías de este comportamiento debido a la compatibilidad con versiones heredadas entre Chrome y WebKit, te recomendamos que revises tu sitio y veas el resumen de los cambios que aparece a continuación.

Resumen de cambios

Si usas hasOwnProperty en una instancia de objeto del DOM, ahora se mostrará false.

A veces, los desarrolladores usan hasOwnProperty para verificar la presencia de una propiedad en un objeto. Esto ya no funcionará como según las especificaciones porque los atributos del DOM ahora forman parte de la cadena del prototipo y hasOwnProperty solo inspecciona los objetos actuales para ver si están definidos en ellos.

Antes de Chrome 42 inclusive, lo siguiente mostraría true.

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

true

A partir de la versión 43 de Chrome, mostrará false.

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

false

Esto significa que, si quieres verificar que isContentEditable esté disponible en el elemento, deberás verificar el prototipo en el objeto HTMLElement. Por ejemplo, HTMLDivElement hereda de HTMLElement, que define la propiedad isContentEditable.

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

true

No estás limitado a usar hasOwnProperty. Recomendamos usar el operando in mucho más simple, ya que este verificará la propiedad en toda la cadena del prototipo.

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

Object.getOwnPropertyDescriptor en la instancia de objeto del DOM ya no mostrará un descriptor de propiedad para los atributos.

Si tu sitio necesita obtener el descriptor de propiedad para un atributo en un objeto DOM, ahora deberás seguir la cadena del prototipo.

Si quisieras obtener la descripción de la propiedad en Chrome 42 y versiones anteriores, habrías hecho lo siguiente:

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

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

En Chrome 43 y versiones posteriores, se mostrará undefined en este caso.

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

undefined

Esto significa que, para obtener el descriptor de propiedad de la propiedad isContentEditable, deberás seguir la cadena del prototipo de la siguiente manera:

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

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

JSON.stringify ya no serializará atributos del DOM

JSON.stringify no serializa las propiedades del DOM que se encuentran en el prototipo. Por ejemplo, esto puede afectar a tu sitio si intentas serializar un objeto como PushSubscription de las notificaciones push.

En Chrome 42 y versiones anteriores, habría funcionado lo siguiente:

> JSON.stringify(subscription);

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

A partir de Chrome 43, no se serializarán las propiedades que están definidas en el prototipo y se mostrará un objeto vacío.

> JSON.stringify(subscription);

{}

Deberás proporcionar tu propio método de serialización, por ejemplo, puedes hacer lo siguiente:

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

Si escribes en propiedades de solo lectura en el modo estricto, se arrojará un error

Se supone que la escritura en propiedades de solo lectura genera una excepción cuando usas el modo estricto. Por ejemplo, considera lo siguiente:

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

En Chrome 42 y versiones anteriores, la función habría continuado y continuaba ejecutando de forma silenciosa la función, aunque isContentEditable no habría cambiado.

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

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

A partir de Chrome 43, se producirá una excepción.

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

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

Tengo un problema, ¿qué debo hacer?

Sigue las instrucciones o deja un comentario a continuación y hablemos.

Vi un sitio con un problema. ¿Qué debo hacer?

Muy buena pregunta. La mayoría de los problemas con los sitios se basan en el hecho de que un sitio haya elegido realizar la detección de presencias de atributos con el método getOwnProperty. Esto ocurre principalmente cuando el propietario del sitio solo se orienta a navegadores de WebKit más antiguos. Los desarrolladores pueden realizar las siguientes acciones:

  • Informa un problema sobre el sitio afectado en nuestra herramienta de seguimiento de errores (de Chrome)
  • Informa sobre un problema en el radar WebKit y consulta https://bugs.webkit.org/show_bug.cgi?id=49739

En general, me interesa hacer un seguimiento de este cambio