Chrome 43からDOMの属性がJSのプロトタイプチェーンに移行します

  • 185
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Chrome 43およびそれ以降のバージョンにおいて、DOMの属性(値)がJavaScriptのプロトタイプチェーンに移行されます。これにより、突っ込んだことをやっていた場合に、今まで動作していたコードが動かないということが発生する可能性がありますので、事前に変更内容を知り、対策をしておくことが求められます。HTML5Rockの更新情報として掲載されていましたので、さっそく日本語訳をしてみました。心当たりがある方は、ぜひ以下の内容を読んでいただいて、コードの修正などに取り込んでいただければと思います。

原文: http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype

DOM属性は今後プロトタイプチェーンに

Chromeチームは、最近「DOM属性をプロトタイプチェーンに移動しています」とアナウンスしていました。この変更(Chrome 43 - Betaは2015年4月中旬)は、Web IDL SpecやIEやFirefoxといった他のブラウザの実装とChromeをより一致させます。SafariのようなWebKitベースのブラウザは、現在その仕様と互換はありません。

新しい振る舞いは、多くの状況において有益です。それは、以下です:

  • 仕様への準拠によって、Web(IEやFirefoxはすでにこれをしている)間の互換性を改善します。
  • 効率的に、そして一貫性を持って、全てのDOMオブジェクトにgetter/setterを作成することができます。
  • DOMプログラミングのハック可能性が高まります。例えば、いくつかのブラウザが実装していない機能を効率的にエミュレートするポリフィルや、デフォルトのDOM属性の振る舞いを上書きするJavaScriptライブラリを実装しやすくなるでしょう。

例えば、仮想のW3C仕様は、Chromeブラウザがまだ実装していないisSuperContentEditableと呼ばれるある新しい関数を含んでいるとした場合、それは"ポリフィル"やライブラリの機能でエミュレートが可能になります。ライブラリ開発者として、有益なポリフィルを作成するために、あなたは以下のようにプロトタイプを自然に使いたくなるでしょう:

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はそれが現在のオブジェクトに定義されているかどうかを確認するためにのみ検査が行われるので、この仕様( http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.5 )に基いて、これはもはや機能しません。

Chrome 42またはそれ以前では、以下のコードはtrueを返すでしょう。

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

true

Chrome 43以降は、falseを返すでしょう。

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

false

これは、もしあなたがそれをチェックしたい場合に、HTMLElementオブジェクトのプロパティをチェックしたいと望む要素でisContentEditableが利用可能になることを今後は意味します。例えば、HTMLDivElementは、isContentEditable属性を定義するHTMLElementを継承します。

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

true

hasOwnPropertyの利用にロックインはされません。私たちは、より単純なinオペランドの使用をお勧めします。これは、プロトタイプチェーン全体で属性をチェックします。

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

DOMオブジェクトインスタンスのObject.getOwnPropertyDescriptorはもはや属性のプロパティデスクリプタを返さないでしょう

あなたのサイトがDOMオブジェクトの属性のプロパティデスクリプタを得ようとしている場合、今後はプロパティチェーンを追う必要があります。

もしChrome42あるいはそれ以前でプロパティディスクリプタを得ようとする時は、以下のようにしていたはずです:

> 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属性を永続化しません。例えば、もしあなたがPush NotificationのPushSubscription( https://w3c.github.io/push-api/#pushsubscription-interface )のようなオブジェクトを永続化しようとした時に、あなたのサイトに影響する可能性があります。

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

strictモードでの読み取り専用プロパティへの書き込みはエラーがスローされるでしょう

strictモードを使っていた際には、読み取り専用プロパティへの書き込みは例外が投げられることになっています。例えば、以下のコードがあったとします:

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メソッドを使って属性の存在検知をすることを選択したという事実に基いていると思います。これはほとんどの場合、サイトのオーナーがSafariのようなWebKitブラウザのみを対象にした場合に起きます。開発者ができることはいくつかあります:

  • 影響を受けたサイトに関して、私たちの(Chromeの)Issue Trackerに問題をまとめて提出してください。
  • WebKit radarに問題をまとめて提出してください。また、https://bugs.webkit.org/show_bug.cgi?id=49739 を参照してください。

私はこの変更を追跡することに広く興味があります