IE9 Platform Preview 4 でマイクロソフトは、JavaScript エンジン Chakra を IE に統合する方法を大幅に設計し直しました。この再設計は、Dean の記事 (英語) でも解説されていますが、IE9 標準モード向けに DOM のプログラミング モデルを少し変更して、新しい ECMAScript 5 機能との整合性の確保、他のブラウザーとの相互運用性の向上、さらに新しい標準である WebIDL (英語) への準拠を実現しています。

この記事では、これらのプログラミング モデルの変更について詳細に説明します。強化された DOM 機能は、最新の Platform Preview ビルドで利用できます。この記事では、Platform Preview 4 と共にリリースされた DOM 強化機能のデモ ページ (英語) を基に、これらの変更点を具体的に紹介していきます。

24 個のパズル ピースがすべて揃って IE のロゴ画像になったところ。これは、IE Test Drive Web ページ http://ie.microsoft.com/testdrive/ で、強化された DOM 機能を実行している IE9 のスクリーンショットです。

このデモは、大きく 4 つに分類される 24 の機能をテストします。

  • ネイティブ JavaScript オブジェクトを継承する DOM オブジェクト
  • JavaScript の機能と DOM オブジェクトの連携
  • 相互運用可能なプログラミング機能
  • DOM オブジェクトに適用される新しい ECMAScript 5 のサポート

この最初の 2 つは関連性が深いため、一緒に説明します。

ネイティブ JavaScript オブジェクトを継承する DOM オブジェクト、JavaScript 機能と DOM オブジェクトの連携

IE9 より前の JavaScript エンジンは、従来の COM バインドによって DOM に接続されていました。このような従来のバインドでは、JavaScript エンジンは DOM のプリミティブなオブジェクトや関数の表現しか使用できませんでした。そのため、開発者の間では、たくさんある基本的 JavaScript 機能をすべてのオブジェクトと関数 (Window、Document、NodeList などの DOM オブジェクトを含む) に使用できることが期待されていましたが、実際にはネイティブの JavaScript オブジェクト (Array、Number など) にしか使用できませんでした。

ECMAScript 標準は、すべての JavaScript オブジェクトで一様に動作すべき基本操作を定めていますが、"ホスト オブジェクト" についてはこの標準仕様からの逸脱を認めています。IE の以前の JavaScript エンジンは DOM オブジェクトを "ホスト オブジェクト" として扱っていたため、プロパティ アクセスなどの基本的な JavaScript 操作の動作が他とは違うことがありました。ECMAScript が認めているとは言え、DOM オブジェクトと JavaScript オブジェクトの間の動作の不一致は、Web 開発者に何らかの対応を迫る相違点となっていました。

たとえば、多くの Web 開発者をよく混乱させるものとして、IE の DOM 関数が、JavaScript の typeof 演算子で "関数" ではなく "オブジェクト" として報告されるという現象がありました (この機能はデモのピース #10 ではっきり確認されます)。

IE9 の標準モードでは、DOM を "ホスト オブジェクト" ではなくネイティブ JavaScript のオブジェクトおよび関数として構築するため、Web 開発者がネイティブ オブジェクトに想定する機能が使用可能になります。

相互運用可能なプログラミング機能

3 番目のグループの機能は、Web 開発者がよくつまずく、IE 独特のプログラミング モデルの動作に関するものです。こうした IE のプログラミング モデル固有の動作のために、Web 開発者は、作成したコードの動作がブラウザーの種類によって異なるという状態に陥ることがありました。

新しい統合アーキテクチャでは、ブラウザーの種類を問わず同一スクリプトで同一の動作を実現することをこれまで妨げてきた不整合な点について、その多くが解消されました。ただしプログラミング モデルが変更されたため、IE 向けに作成された条件付きコードを含むサイトが IE9 ではこれまでと異なる動作をする可能性があります。したがって、これらの変更は重要なので以下に詳細に説明します。

関数が列挙の対象に

IE8 以前は、DOM オブジェクトの列挙に、その DOM オブジェクトのメンバー関数は含まれませんでした。IE9 は、"enumerable" プロパティ記述子の値が 'true' に設定された DOM オブジェクトのすべてのプロパティを列挙するようになりました。(つまり、列挙をプログラムで操作することが可能になりました。) 関数は既定で列挙されます。これは、他のブラウザーでの動作と一致します。

暗黙の関数呼び出しの削除

これまでのバージョンの IE において DOM 関数は非常に特殊でした。これらの関数は、typeof で "オブジェクト" と判断されるだけでなく、所属先のオブジェクトを参照する静的 'this' 値を維持していました。そのため、DOM 関数の参照をキャッシュし、'this' 値を明示的に渡すことなくその関数を呼び出すことができました。

// Works in IE8 and earlier versions
// Doesn't work in IE9 or other browsers

var cachedGetElementById = document.getElementById;
cachedGetElementById('value');

IE9 では、このコードは、他のブラウザーと同様に例外がスローされるようになりました。これまでこの IE の動作に依存していたコードは、".call" による回避策を採ることができます。

// Works in IE8/IE9 and other browsers
// Doesn't work in IE7 and earlier versions;

var cachedGetElementById = document.getElementById;
cachedGetElementById.call(document, 'value');

ECMAScript 5 は、これまで IE によってサポートされていたプログラミング特性を持つ機能として "bind" メソッドを提供しています。

// Works natively in IE9 because of ECMAScript 5's 'bind' API
var cachedGetElementById = document.getElementById.bind(document);
cachedGetElementById('value');

DOM 例外と 'const' プロパティのサポート

IE9 の強化された DOM には、W3C 指定の DOM 例外オブジェクト (英語) と、Web 開発者が DOM API エラーの内容を (大まかに) 判断するために使用できる標準エラー コードが追加されました。プログラムの可読性を高めるため、こうしたエラー コードはわかりやすく定義された 'const' プロパティと照らし合わせるのが一般的です。


catch(ex) {
   if (ex.code == DOMException.INDEX_SIZE_ERR)
      …
}

強化された DOM は、DOM 例外をスローおよびキャッチするアーキテクチャと共に、事前に定義された 'const' プロパティを提供します。

toString 動作の整合性

Chakra と DOM が完全に統合されたため、DOM は toString (オブジェクトを文字列形式に変換するための関数) について独自の実装を持たなくなりました。以前の DOM が実装していた toString は、JavaScript に組み込まれていた実装と似ていましたが、同じではなく、矛盾した結果や不可解な結果を返すことがたびたびありました。IE9 の DOM オブジェクトは、JavaScript に組み込まれている toString を継承して使用することで、より一貫した結果を返すようになりました。

プロパティの記憶域と属性の記憶域の分離

これまでのアーキテクチャでは、DOM オブジェクトは独自のプロパティ記憶域を持っていました。このプロパティ記憶域は、属性 (HTML マークアップ内の属性) の記憶域と場所が同じでした。IE9 の新しいアーキテクチャでは、要素の属性記憶域と、要素のスクリプト オブジェクトに割り当てられる動的プロパティの記憶域が分離されました。以下のマークアップを例として、この分離を具体的に説明します。

<div id="myId" class="c" user-defined-attribute="test">

上のサンプルで、"id"、"class"、および "user-defined-attribute" は属性です。div 要素の JavaScript オブジェクトは、同様のプロパティを公開します。

// Get the JavaScript object representing the body
var divOb = document.getElementById(‘myId’);
divOb.id;        // "myId"
divOb.className; // "c"

これらの JavaScript プロパティは、要素の属性リストに格納されている値を取得します。たとえば、"id" は "id" 属性の値を取得し、"className" は "class" 属性の値を取得します。

これまでのバージョンの IE では記憶域の場所が共有されていたので、プロパティが動的に追加されるとそれは要素の属性リスト内に魔法のように現れていました (逆の場合も同じです)。このため、予期しない結果になることがありました。

<div id="myId" class="c" user-defined-attribute="test">

var divOb = document.getElementById("myId");
// The next statement unexpectedly adds "userProperty" as
// an attribute to the element.

divOb.userProperty = "test"

// How many attributes?
alert("Total attributes = " + divOb.attributes.length);

IE9 やその他のブラウザーでは、属性の総数を 3 ("id"、"class"、"user-defined-attribute") と通知しますが、これまでのバージョンの IE は、これに "userProperty" を加えて 4 と通知します。逆のパターンとして、以下のようにユーザー定義属性が動的プロパティとして表示されることを想定するコードもよく使われています。

<div id="myId" class="c" user-defined-attribute="test" userAttribute="test">

var divOb = document.getElementById("myId");
// Get the "userAttribute" and "user-defined-attribute" value
// (only worked in IE8 and previous versions)
var value1 = divOb.userAttribute;
var value2 = divOb["user-defined-attribute"];

この従来の IE の動作を想定したコードは多くの場面で使われていると思われます。相互運用性のある方法で、既知でない属性を取得するには、次のように "getAttribute" を使用します。

var value1 = divOb.getAttribute("userAttribute");
var value2 = divOb.getAttribute("user-defined-attribute");

同様に、動的プロパティを属性コレクションを使用して照会することも推奨されません。

ECMAScript 5 の新機能

最後のグループの機能テストは、Chakra の ECMAScript 5 実装で提供される新しい機能が DOM に適用されていることに関するものです。IE9 の DOM 強化が掲げる大きな目標の 1 つは、ECMAScript 5 言語セマンティクスの中で論理的意味を持つ DOM の表現を提供することでした。これはそれほど難しいことではありませんでした。というのも、そもそも ECMAScript 5 の大きな目標の 1 つが DOM オブジェクトが必要とする機能性のサポートを向上させることだったからです。今回の実装では、アクセサー (getter/setter) プロパティを広範に使用するなど、できるだけ多くの場合でネイティブ ECMAScript 5 言語機能を使用して DOM を表現するようにしました。

このようなネイティブな統合により、新しい ECMAScript5 機能のすべてが、ネイティブ オブジェクトとも DOM オブジェクトとも同じように連携できるようになりました。

強化された DOM 機能のデモでは、DOM が Chakra のような ECMAScript 5 対応 JavaScript エンジンと完全に統合されている場合に実現されることの中から、24 個だけサンプルとして紹介しています。私たちは、IE9 でこれをサポートできることを大変喜んでいると共に、ECMAScript 言語のバインドにおいて異なるブラウザー間での相互運用性が向上することを願っています。W3C 内でこのようなバインドを標準化することは重要なステップです。私たちは、その作業に貢献できて嬉しく思います。

W3C の Web 標準は、標準 IDL (インターフェイス定義言語) を JavaScript オブジェクトに変換するための方法として、常に ECMAScript 実装のための言語バインド (英語) を提供してきました。ただし、単純な "ホスト オブジェクト" (ECMAScript 言語機能全体を考慮していないバインド) 以上のバインドを作成するには、細かい部分が十分ではありませんでした。他のブラウザーは、単純な "ホスト オブジェクト" よりはるかに包括的な言語バインドを備えていますが、統合の不整合性は残っています。このような不整合は、基本的な言語サポートの上に抽象的なレイヤーや機能を構築しようとしている JavaScript フレームワーク開発者にとって大きな障壁となります。不整合を解消して欲しいという要望から、WebIDL (Web インターフェイス定義言語、英語) と呼ばれる標準が提案されました。WebIDL 仕様には、WebIDL を使用して記述された W3C 仕様を JavaScript オブジェクトに変換する方法が、きわめて詳細に記述されています。

今後の記事では、WebIDL の使用方法をさらに詳細に説明して、IE9 の強化された DOM の開発に関する情報を提供していく予定です。

IE9 で強化された DOM をぜひお試しください。皆さまからのご意見、ご要望を心からお待ちしております。

Travis Leithead
IE プログラム マネージャー