標準仕様に含まれない機能を実装するかしないか、マイクロソフトがどのように判断していると思いますか。すべてのブラウザー プロバイダーと同様に、我々もこの決定を下す場面に何度も遭遇します。この記事では、実際の JavaScript の例を使用して、標準仕様が不十分な場合でも相互運用性のあるブラウザーを提供するためにマイクロソフトが原則として使用している概念をいくつか説明します。

理想は、標準仕様がすべての機能を完全に網羅していることです。JavaScript の実装であれば、本来は、ECMAScript 仕様 (英語) のすべてを含むようにすればよいだけで、それ以外のことを考える必要はありません。マイクロソフトでは、Web ブラウザー間の相互運用性を実現するためには、Ecma International (英語)W3C (英語) などの標準化団体 (英語) から公開される仕様が最も重要だと考えています。ブラウザーの実装者がどのような機能を提供し、Web 開発者がどのような機能を使用できるのか、といったことがすべて仕様に示されていれば、それが理想的です。

しかし、実際の Web 環境はそれほど単純明解ではありません。仕様が完璧であることはほとんどなく、意図的に不完全またはあいまいにされていることもあります。私は ES5 (ECMAScript 5) 仕様の編集にかかわったこともあるのですが、完全には解決されない問題が常に存在していることも承知しています。結局、どの標準仕様にも定義されていないが、広く実装され使用されている機能がいくつも存在するというのが現在の状態です。既存の Web が動作するブラウザーを構築しようとするなら、標準仕様で定義されていないさまざまな機能を実装する必要があるのです。

正規表現文法の実例

我々は、正規表現文法を含む ES5 仕様に忠実に従って Chakra (英語) を構築しました。しかし、実際の Web サイトでテストしてみると、一部の正規表現リテラルが構文エラーになり、いくつかのページが機能しないことがわかりました。たとえば、次のような右角かっこを含む正規表現は失敗しました。

next = /]/.exec(buffer);

ECMAScript 標準の正規表現文法では、] が検索文字として直接使用できないことが原因でした。仕様によれば、/]/ は無効で、代わりに /\]/ と指定する必要があります。重要なのは、[ が同じように制限されているということです。そうでないと、/axyz[a-d]qwer/ のようなリテラルを解析できなくなるからです。ただ実際には、前に [ がない単独の ] であれば解析の問題は生じません。それでも標準は、単独の ] を無効としています。

実際には、すべてのブラウザーが /]/ などの正規表現を受け入れ、Web 開発者もそのように記述しています。

マイクロソフトでは、標準には含まれていないが、広く実装されていて相互運用性のある機能のことを、独自にコンセンサス機能と呼んでいます。一般に、IE9 などの新しい JavaScript の実装では、実際の Web コンテンツを扱うために、このようなコンセンサス機能を実装する必要があります。コンセンサス機能は標準仕様には記述されていないものなので、まずはそのような機能を特定することが大きな課題となります。

マイクロソフトは、このようなコンセンサス機能をサポートする必要があることを認識しているのと同時に、将来的に、該当する Web 標準にこうした機能が追加されるように努めています。とはいえ、必ずしもすべての JavaScript 拡張機能が広く一般に、また一様に実装されているとは限りません。

なぜ IE9 には _ _ メソッドがないのか

ES5 では、getter/setter メソッドがサポートされるようになりました。しかし一部の Web ブラウザーでは、これと同等の機能がかなり以前から使用可能でした。これまで、ブラウザー開発者は、これらのメソッドを定義するためにさまざまな構文や API を試してきました。ES5 以前、getter/setter メソッドを定義するために最も広く実装されていた API では、__defineGetter__ と __defineSetter__ という名前のメソッドが使用されていました。2 つのアンダースコア文字でメソッド名を囲むという方法は、そのメソッドが実験的な機能であること、または特定の JavaScript 実装に固有の下位レベル機能を使用するメソッドであることを明確にするために一部のブラウザー開発者が使用してきた規則です。

ECMA TC39 (英語) のメンバーは、getter/setter メソッドの価値が証明されたため、これらのメソッドを ES5 仕様に追加することに合意しました。ただし、TC39 は、__defineXXX__ 形式の API を標準とするのではなく、それらの代わりとなる新しい API を設計することに決めました。

なぜ TC39 はそうしたのでしょうか。理由は、その API を提供していた各ブラウザーの動作に大きな違いがあったからです。これらのメソッドに共通するセマンティクスに基づいて標準化を行うと、その新しいセマンティクスに準拠するために、一部またはすべての既存ブラウザーが実装を変更する必要が出てきます。これは、それらのブラウザーで動作するように作成されていたアプリケーションが動作しなくなるということにもなります。

もう 1 つの理由として、名前付け規則の一貫性の維持がありました。ECMAScript に組み込まれている名前で、他にアンダースコア文字で開始、終了するものはありません。このような名前が実験的な機能または特定の実装固有の機能に付けられていることは広く知られています。したがって、標準化された機能でこの規則を使用すると、誤解を招きやすい上に、今後、実験的機能にこの規則を使用する有用性が失われてしまいます。

TC39 は、Object.defineProperty メソッドに基づいて新しい API を開発しました。この API は、getter/setter プロパティの定義をサポートするだけでなく、ES5 の他の新しい機能もサポートします。__defineXXX__ 形式のメソッドは、ES5 仕様から除外されました。

既に __defineXXX__ API を提供しているブラウザーは、互換性の理由から、これらのメソッドを変更せずに引き続きサポートすることができます。ただし、ES5 標準をサポートするブラウザー間で相互運用が可能になるように新しいコードを記述するには、Object.defineProperty を使用して getter/setter プロパティを定義する必要があります。

現在でも、IE9 に __defineXXX__ API を追加してほしいという要望があります。理由はわかっています。一部の開発者は、これらの API を使用するコードに依存しており、そのコードを IE9 でも実行したいからです。しかし、これらの API をサポートすることは、相互運用性のある Web の実現のためには得策ではないと考えます。TC39 は、既にこの API について検討し、標準化する必要はないという結論を下しました。IE9 に __defineXXX__ API のサポートを追加すれば、これが Web の恒久的なコンセンサス機能となってしまう可能性もあり、ひいては TC39 の決定を根本から無視することになり兼ねません。

短期的には、従来の API を使用している開発者には多少の追加作業が必要になりますが、__defineXXX__ API を実装しているコード用に、Object.defineProperty を使うための単純な互換性ライブラリを作成するのはとても簡単なことです。

問題を含むコンセンサス機能

JavaScript コードでは、実際の関数宣言より前にあるコードで、その関数を参照して呼び出すことができます。これが可能になるのは、JavaScript では論理的に、すべての関数宣言がそれを含むコード本体の先頭に移動されるためです。JavaScript では、同じ関数名で複数の関数宣言を置くこともできます。簡単なテスト関数を見てみましょう。

function testFuncDeclarationOrder () { f1(); function f1() {alert("first f1")} f1(); function f1() {alert("second f1")} f1(); }

この関数を呼び出すと、"second f1"、"second f1"、"second f1" という順番で 3 つのアラートが生成されます。このような結果になるのは、JavaScript の実際の処理が、関数が次のように記述されているものと解釈しているからです。

function testFuncDeclarationOrder () { function f1() {alert("first f1")} function f1() {alert("second f1")} f1(); f1(); f1(); }

従来より ECMAScript 仕様は、関数宣言の配置に 1 つだけ制限を設けています。ECMAScript 標準では、制御構造ステートメントの本体内に関数宣言を配置することはできません。例を次に示します。

if (someCondition) { function f1() {alert("f1")} }

上のコードが ECMAScript 仕様に基づいて解析されると、構文エラーが生成されます。しかし、どのブラウザーで実行してみても、エラーにはなりません。ステートメントが許可される場所ならどこでも関数宣言をサポートすることは、Web のコンセンサス機能です。では、なぜそれが ES5 に含まれていないのでしょうか。これに関して、ES5 仕様のセクション 12 に説明が書かれています。

注記: 広く普及している各種の ECMAScript 実装の中に、ステートメントとしての関数宣言の使用をサポートするものがあることは既知です。ただし、このような関数宣言が持つ動作の意味は、各種の実装間で大きく異なり、一致させることができません。このような一致させ難い相違点があるため、関数宣言をステートメントとして使用すると、そのコードは実装間で正確に移植することができないコードとなります。ECMAScript 実装で推奨される処理は、このような関数宣言の使用を許可しない、またはこのような使用が検出された場合に警告を出すのいずれかです。ECMAScript の今後の改訂版では、ステートメントのコンテキストで関数を宣言しても移植性を損なわない方法が別途定義される可能性があります。

この説明は何を意味しているのでしょうか。簡単に言うと、ブラウザー間でかなり動作が異なっていたため、それらを一致させることが現実的ではなかったということです。

それでは、IE9 ではどう処理されるのでしょうか。ES5 で推奨されているとおり、このようなステートメント レベルの関数宣言は許可されないのでしょうか。いいえ。このような宣言は、IE のこれまでのバージョンとまったく同様に処理されます。

なぜそのように決定したのでしょうか。ブラウザー実装者たちのこれまでの経験から、このような宣言を拒否してしまうと、既存の Web ページの多くが機能しなくなることがわかったからです。

これは意外に思われるかもしれません。ブラウザーによって動作が異なる機能に依存しながら、しかもブラウザー間で相互運用が可能な Web ページなどあるのでしょうか。1 つには、このコードは実際には使用されない可能性があります。このコードが置かれている関数は、呼び出されることがないか、その結果がページの操作に必要でないのかもしれません。ただし、JavaScript 実装がこれらの関数を構文エラーとして処理すれば、そのコードが呼び出されなくても、スクリプト全体が拒否されます。

開発者を悩ますこのような機能の実装は好ましいことではないのですが、この機能の存在は Web で一定の地位を得ているため、マイクロソフトとしては選択の余地がありませんでした。

const について

IE9 について、const 宣言をサポートするのかどうかを尋ねられることがあります。const は、"名前付き定数" 値を宣言する手段を提供する非標準の JavaScript 拡張機能です。典型的な使い方は次のようなものです。

const pi=3.14159;

いくつかのブラウザーがこの機能をサポートしていますが、例外的な場合の処理や何をエラーと定義するかなどについてはブラウザーごとに大きな違いがあります。const を "サポート" するブラウザー間の動作の違いの例を次に示します。

// a function with multiple const declaration for the same name function f() { alert('executing within f'); const x=1; //declare constant x as 1 alert(x); const x=2; //redeclare constant x as 2 alert(x); x=3; //try to assign 3 to x alert(x); } alert('about to call f'); f();

このコードの実行結果は、ブラウザーによって次のように変わってきます。

  • 構文エラーを出し、ページを読み込まない。
  • 関数 f が呼び出された時点で例外をスローする。
  • '1'、'2'、'2' とアラートを表示する。つまり、const 宣言の競合は許可されるが、代入は無視される。
  • '1'、'2'、'3' とアラートを表示する。つまり、const が var と同然に処理される。

基本的には、const 宣言のきわめて単純な使用方法だけが、その宣言をサポートするブラウザー間で相互運用性があるということになります。複雑な使用方法や、エラーの可能性を含むシナリオなどについては、ブラウザー間での相互運用性がありません。

実際の JavaScript 実装では、ごく普通の使用方法だけをサポートするというわけにはいきません。実際の実装では、きわめてまれなケースやエラーの場合も含めて、現実の Web ページに存在するすべてのシナリオに対して何らかの動作を行う必要があります。仕様の標準化は、ほとんどがこのような状況に対応するために行われてきたとも言えます。通常、単純で一般的である使用方法に対してどのような処理を行うべきかは自然と明らかになっています。各実装で相互運用性を実現するために標準仕様が必要になるのは、一般的でないまれなケースについてです。

TC39 は、ES5 に const を入れることを真剣に検討し、初期の ES5 ドラフトには入れていました。しかし、それをどのように定義すべきか、他の宣言とどのように関係させるかについては、多くの問題がありました。最終的に、const の標準化は、仕様の次版まで待つべきであると TC39 内で意見が一致しました。次版では、他の問題のいくつかも一緒に解決できる可能性があります。基本的に、TC39 は、問題を抱えた機能については標準化しないことに決めています。const の標準化は、設計を改善して同一の設計をすべてのブラウザーに組み込むことができるように、将来に延期されることになりました。

IE9 は const をどう扱っているのでしょうか。今のところ、我々の決定では const をサポートしません。const は、すべてのブラウザーで使用できるものではないため、まだコンセンサス機能とは言えません。標準仕様はなく、どの既存ブラウザーの実装でも意味的に大きな違いがあります。さらに、TC39 は、既存の方法をどれも標準化せず、ECMAScript の今後の改訂で再検討することに決定しました。

多くの Web 開発者がこのような宣言を必要としていることは理解しています。しかしながら、上記の関数宣言と同じようなことにはしたくありません。機能としては存在するが相互運用性を考慮するなら使用しない方が良いというような、条件付きの機能は好ましくありません。結局、現在のところは標準に含めず、標準化プロセスの進展を待つことが、Web にとっての長期的な最善策と思われます。

原則に基づく意思決定

この記事では、JavaScript からのみ 4 つだけ例を挙げて、IE9 の作成においてマイクロソフトがどのような判断をしたのかを説明しました。他の Web 標準やコンセンサス機能についても、同様の問題が数多くあります。それぞれの問題に対して、マイクロソフトは同様の分析を行います。標準にはどう記載されているか。どれくらい多くの Web サイトが実際にその機能を必要としているか。共通のセマンティクスを含むコンセンサス機能か。互換性のない複数のバリエーションが存在するか。標準化にかかわる団体がそれを検討しているか。テスト スイート (英語) は存在するか。私たちの採用が、その標準の進展に役立つか、それとも妨げになるか。最終的にはさまざまな事項を考慮して決定を下しますが、私たちは原則に基づく一貫した意思決定を行うように努めています。

 

 

Allen Wirfs-Brock
マイクロソフト JavaScript 言語設計担当