IE9 でのスクリプトの強化: ECMAScript 5 のサポートとその関連事項
本記事は、マイクロソフト本社の IE チームのブログ から記事を抜粋し、翻訳したものです。
【元記事】Enhanced Scripting in IE9: ECMAScript 5 Support and More (2010/6/26 6:50 AM)
※担当者註 : この記事は 2010 年 6 月の記事ですか、資料性が高いと判断されたため投稿されました。
これまでは、Internet Explorer 9 での JavaScript パフォーマンスの向上 についての説明が多く、“Chakra” エンジンの言語機能の新機能や変更点についてはあまり説明をしていませんでした (リンク先はいずれも英語)。
今回は、Platform Preview 3 での JavaScript の機能強化について説明したいと思います。この機能強化は、皆さんもご自分でお試しいただけます (英語)。
JavaScript 言語を定義する業界標準は、ECMA International によって策定、公開されている ECMA-262: ECMAScript 言語仕様です (リンク先はいずれも英語)。
1999 年12 月に ECMA-262 第 3 版 (英語) が公開され、昨年をもって既に 10 年以上が経過しています。2009 年 12 月、ECMA は第 3 版の後継として ECMA-262 第 5 版 (英語) (第 4 版は公開されていません) を承認しました。
マイクロソフトでは、昨年、ネイティブ JSON サポートを IE8 に追加 (英語) すると同時に、ECMAscript 5 (ES5) の要素のサポートを開始しました。JSON の他にも、ES5 では多くの重要な拡張機能が JavaScript 言語に標準化されています。
IE9 Platform Preview での新しい ES5 機能
IE9 標準ドキュメント モード (英語) では、以下のような ES5 の重要機能が数多く実装されています。
新しい Array メソッド。 配列を操作する新しいメソッドが 9 つ用意されています。そのうちの 2 つ (indexOf
と lastIndexOf)
は、配列内で特定の値の検索を行います。この 2 つの関数の働きは、文字列を操作する同じ名前の関数と同様です。残りの 7 つの新しい Array メソッドは、関数と同じプログラミング スタイルで配列を操作できます。たとえば、次のコードでは、新しい filter
メソッドを使って、特定の条件を満たす配列要素を収集しています。
// メニュー アイテム オブジェクトが有効か無効かを判定する関数
function enabled(menuItem) {return menuItem.status==="enabled"};
// 各メニュー アイテムが status プロパティを持ち
// メニュー オブジェクトが items プロパティを配列として持つことが前提
// 有効なメニュー アイテムのみ格納した新しい配列を作成
var enabledItems=myMenu.items.filter(enabled);
このようなメソッドを使えば、コードで明示的なループを記述せずに、さまざまな配列処理を実行できます。さらに、これらのメソッドはすべて汎用メソッドです。つまり、これらは、配列コンストラクターを使って作成されたオブジェクトだけでなく、数値インデックスのプロパティを持つすべてのオブジェクトに適用できます。これらのメソッドを使用したデモ (英語) を IE9 Test Drive (英語) サイトでご覧になれます。下表にメソッドの概要をまとめます。
Array メソッド |
説明 |
indexOf |
配列内で、特定の値が最初に出現する位置を検索します。 |
lastIndexOf |
配列内で、特定の値が最後に出現する位置を検索します。 |
forEach |
配列内の各要素に対して関数を適用します。 |
every |
配列内のすべての要素に対して特定の条件が真かどうかを判定します。 |
some |
配列内に、特定の条件が真である要素が少なくとも 1 つあるかどうかを判定します。 |
map |
配列内の各要素に対して関数を適用し、その結果を格納した新しい配列を生成します。 |
filter |
特定の条件が真となる配列内のすべての要素を新しい配列に格納します。 |
reduce |
配列内のすべての要素を累算して単一の値にします。 |
reduceRight |
配列内のすべての要素を最後の要素から順に累算して単一の値にします。 |
拡張オブジェクト モデル。 この分野で最も重要な新機能は、アクセサー プロパティです。アクセサー プロパティは、プログラムがプロパティ値を取得 (get) または設定 (set) するときの処理を JavaScript プログラマが制御できるようにするものであるため、“getter/setter” プロパティと呼ばれることもあります。
ES5 の拡張オブジェクト モデルでは、個々のプロパティの値を変更できるどうかか、for ~ in ステートメントで値を列挙できるかどうか、さらにプロパティを削除または再定義できるかどうかをプログラマが制御できます。また、新しいプロパティをオブジェクトに追加できるかどうかも制御できます。ES5 では、JavaScript プログラマが特定のプロトタイプ オブジェクトを継承するオブジェクトを作成したり、オブジェクトのプロパティ定義を検査、操作したりすることも簡単になっています。
このような拡張オブジェクト モデルの機能はすべて、Object コンストラクターの新しい関数プロパティから利用できます。ただし、IE9 Platform Preview の現時点で最新のリリースは DOM オブジェクトでのこれらのメソッドの使用をまだ完全にはサポートしていないので注意してください。
Object 関数 |
説明 |
Object.defineProperty |
プロパティ定義を作成または変更します。プロパティは、データまたはアクセサー プロパティのいずれかとして定義でき、 |
Object.defineProperties |
複数のプロパティ定義を一括で作成または変更します。 |
Object.create |
指定したプロトタイプと指定した一連のプロパティ (オプション) で新しいオブジェクトを作成します。 |
Object.getPrototypeOf |
引数に指定したオブジェクトのプロトタイプ オブジェクトを取得します。 |
Object.getOwnPropertyDescriptor |
オブジェクトのプロパティ属性の完全な説明を返します。 |
Object.getOwnPropertyDescriptor |
オブジェクトの非継承プロパティのすべての名前を格納した配列を返します。 |
Object.keys |
for ~ in ステートメントでループ処理できる、オブジェクトの非継承プロパティのすべての名前を格納した配列を返します。 |
Object.seal |
引数に指定したオブジェクトへの新たなプロパティの追加を禁止し、既存のすべてのプロパティの削除と再定義も禁止します。個々のプロパティの値は、そのプロパティの |
Object.freeze |
引数に指定したオブジェクトへのプロパティの追加を禁止し、既存のすべてのプロパティの削除と再定義も禁止します。また、既存のプロパティの値も変更不可になります。 |
Object.isSealed |
オブジェクトが Object.seal を使って保護されているかどうかを判定します。 |
Object.isFrozen |
オブジェクトが Object.freeze が適用されているかどうかを判定します。 |
Object.preventExtensions |
オブジェクトに新たなプロパティを追加することを禁止します。 |
Object.isExtensible |
オブジェクトへの新たなプロパティの追加が可能かどうかを判定します。 |
その他の演算メソッドと演算関数。 新しい Array メソッドと Object メソッドの他にも、ES5 では、有用な演算操作を行ういくつかのメソッドが追加および拡張されています。
メソッドまたは関数 |
説明 |
String trim |
文字列の先頭と末尾にあるホワイト スペースを取り除きます。 |
Date toISOString |
Date を、すべての ES5 実装がサポートしていなければならない文字列形式に変換します。 |
Date.parse |
toISOString によって作成された形式を認識するよう拡張されました。 |
Date.now |
数値タイムスタンプを返します。 |
Array.isArray |
オブジェクトが Array であるかどうかを正確に判定します。 |
Function bind |
関数の一部の引数を固定値にプリセットします。 |
ES5 には、この他にも言語に対するマイナーな変更や技術的な修正が数多く含まれています。そのほとんどは、これまでブラウザーが常にサポートしてきたマイナーな機能を標準化しただけのものであり、多数の JavaScript プログラマに影響するようなものではありません。
このような機能の例としては、文字列リテラル内での行連結文字があります。また、マイナーな変更で、もう少し注目すべきものもあります。if``、``super``、`` public ``などの
予約名をオブジェクト リテラル内や、プロパティ アクセスでピリオドの後に続くプロパティ名として使用できるようになったのです。
これまでは、プロパティ名として気軽に使用できない、ばらばらでたくさんの単語に注意しなければなりませんでしたが、この変更によりその必要がなくなりました。
" 同一のスクリプト、同一のマークアップ "
IE9 で JavaScript 実装が更新されたことで、単に新しい ES5 機能がサポートされるようになっただけなく、Web 開発者は IE9 と他のブラウザーで同一のマークアップおよびスクリプトを使うことができるようになりました。
今年の早い時期に、IE8 で実装された JavaScript が ECMAScript 仕様 (第 3 版) とどのように異なるのかを細かく説明する文書を公開 (英語) しています。IE9 の標準モードでは、このような相違点を詳細に調査し、IE9 が他のブラウザーと同じスクリプトを実行できるように変更を行っています。
修正された問題 |
例 |
関数式が関数宣言のように処理される。 |
|
関数式内の関数名が、関数本体の中でローカルに定義されない。 |
var fact="the web is big"; Math.factorial=function fact(n) {return n<=1?1:fact(n-1)}; alert(Math.factorial(9)); // IE8 では例外が発生する。 |
catch 句のパラメーターが外側のスコープで認識できる。 |
var e = "outer"; try {throw "inner"} catch(e) {}; alert(e); // IE8 は誤って "inner" と |
実行時の例外スローが標準の仕様と異なるケースが多い。 |
var obj; //obj の値は定義されていない。 try {alert(obj.prop)} catch (e) { if (e instanceof ReferenceError) alert("correct") else if (e instanceof TypeError) alert("IE8 wrong") } |
配列リテラルの末尾のカンマが、配列の長さに加算される。 |
var len = [1,2,3,].length; alert(len); // 正しくは 3。IE8 では 4。 |
配列リテラル内に空の要素があっても疎な配列にならない。 |
var a=[0,,2,,4]; alert(a.hasOwnProperty(1));// IE8 は誤って true を返す。 |
DontEnum 属性が自身のプロパティによって継承される。 |
var obj={valueOf:0, toString:1,foo:2}; var n=0; for (var p in obj) n++; alert(n); // IE8 は 1 と表示。正しくは 3。 |
\v が、垂直タブ制御文字のエスケープ シーケンスとして認識されない。 |
alert("\v"==="v");// IE8 は true、正しくは false。 alert("\v"==="\u000b"); //IE8 では false。正しくは true。 |
グローバル オブジェクトが Object.prototype から継承されない。 |
alert(hasOwnProperty===undefined); // IE8 では誤って true を表示。正しくは false。 |
正規表現内で捕捉括弧が満たされない場合、undefined ではなく、空の文字列が生成される。 |
var x=/((a)|(ab))((c)|(bc))/.exec("abc"); // x は、正しくは次のようになる。 // ["abc","a","a",undefined, "bc",undefined, "bc"] // IE8 は ["abc","a","a","","bc","","bc"] を生成。 |
toFixed が誤って一部の範囲の値を丸める。 |
alert((0.09).toFixed(1)); // 正しくは 0.1 と表示される。 // IE8 は 0.0 と表示。 |
“同じスクリプトを実行する” という目標は、単に Internet Explorer で実行できるだけではなく、IE を使って開発およびテストしたスクリプトが、ユーザーが使用している他のすべての標準準拠ブラウザーでも実行できることを目指しています。
このような目標達成の妨げとなるのが、他のブラウザーにはないが IE だけに備わっている不要な機能です。
ブラウザーにとって不可欠な固有の機能性や独自の価値があるわけでもなく、1 種類のブラウザーにだけ実装されていて、Web 標準の一部になる見込みも低いとあれば、まさにお荷物同然です。開発者がそのような機能をうっかりスクリプトで使用していると、他のブラウザーではそのスクリプトを実行できません。
以前から Internet Explorer の JavaScript 実装にはこの種の機能がいくつかありましたが、IE9 標準モードでは削除されています。これらのほとんどは、IE の開発初期に、使いやすさの向上を目的として導入されたものです。しかし、他のブラウザーでは採用されず、現在では、ECMAScript 標準に含められる見込みもないことがはっきりしています。
このような機能の例として、コード ブロックの後にセミコロンを置くことができなくなりました。たとえば、IE ではこれまで if ステートメントを次のように記述することが可能でした。
if (conditionMet) {performTrueAlternative()};
else {performFalseAlternative()};
最初の行の末尾にセミコロンがあることに注意してください。ECMAScript 標準では、この位置にセミコロンを置くことは許されません。
このような行を含むスクリプトを IE 以外のブラウザーで読み込もうとすると、構文エラーが検出されスクリプト全体の読み込みが失敗します。この機能の当初の目的は、構文エラーがあってもそれを寛大に受け入れ、透過的に処理することでした。
残念ながら、こうした寛大さがスクリプトを他のブラウザーで実行したときに相互運用性の問題が生じる原因となっています。このような場合は、エラーを報告して、スクリプト開発者に修正を促してください。
IE では、関数宣言構文の拡張がいくつか導入されています。その 1 つは、関数宣言でオブジェクトのメソッド プロパティを直接定義することを可能にします。次に例を示します。
function String.prototype.firstChar() {return this.substring(0,1)};
これは、次のコードと同じです。
String.prototype.firstChar = function (){return this.substring(0,1)};
この他に、1 つの関数宣言で同じ関数に複数の名前を定義できる拡張もあります。コードを次に示します。
function declaration,dcl() {return processDeclaration)()};
同じ関数の短い名前と長い名前を定義しています。上記のどの拡張も、言語の標準機能では実現できない機能性を提供しているわけではありません。また、標準の一部でもなく、他に実装しているブラウザーもありません。したがって、IE9 標準モードでは削除されます。
ただし、IE の JavaScript 実装に固有の機能すべてを削除しようとしているわけではありません。開発者が Internet Explorer や Microsoft Windows 固有の機能にアクセスしたい場合に必要な機能もあります。たとえば、ActiveX オブジェクトへのアクセスをサポートする JavaScript 機能などがそうです。
達成度をテスト
IE9 の第一の目標は、ブラウザー間で同一のマークアップを使用できるようにすることです。もちろん、これには JavaScript コードも含まれます。では、この目標がどの程度達成されているかを知るにはどうすれば良いのでしょうか?
先日の Internet Explorer ブログ記事 (英語) で、JavaScript テスト スイートの標準化に対するマイクロソフトの姿勢を説明しました。
私たちは、Web 標準を策定する組織は、同一のスクリプトおよびマークアップがすべてのブラウザーで機能することを確認できる、信頼性の高いテスト スイートも公開すべきであると考えています。JavaScript についてはまだ標準テスト スイートがありません。しかし、ECMAScript の標準委員会はテスト スイートを作成することを決定しており、ECMA メンバー (英語) である他のブラウザー ベンダーと共同で作業を進めています。このテスト スイートはまだ完成しておらず公開されていません。当面は、私たちが使用し ECMA に提供する予定の 1300 件以上の ES5 関連のテストをInternet Explorer Testing Center (英語) でご利用いただけます。IE9 は、ES5 のサポートが追加された結果、Acid3 (英語) の Bucket 6 のテスト (JavaScript に関するテスト) 16 件すべてに合格しています。
ぜひお試しください
皆様からのフィードバックをお待ちしております。JavaScript のバグを見つけたら、ぜひお知らせください。特に、相互運用性に関する問題については高い関心を持っています。標準の JavaScript 機能を使っていて、Platform Preview の IE9 標準モードと他のブラウザーで動きが異なる場合、バグの可能性があります。Connect からご報告ください。今回説明した変更は IE9 標準モードのドキュメントにのみ適用されるもので、IE9 の互換モードで実行される Web サイトは、変更なしでこれまでと同様に正常に動作します。
最後に、既存のコードで IE JavaScript の既知の相違点やバグに基づいたブラウザー検出を行っている場合は、コードを慎重に見直してください。そうした検出方法は、IE9 標準モードでは機能しなくなる確率が高くなります。IE9 標準モードは、これまでの IE JavaScript とは異なっています。
Allen Wirfs-Brock
Microsoft JavaScript 言語アーキテクト