目次


ECMA-262 第 5 版

最新の JavaScript 仕様に関する調査

Comments

ECMA-262 第 5 版という無味乾燥なタイトルで公開された標準 (以下、ES5 と呼びます) は、ECMAScript 仕様の最新版です。ECMAScript は、今日の Web で最も重要な言語である JavaScript のベースになっている標準です。JavaScript は Adobe ActionScript (およびそのバリエーション) の基礎にもなっているため、ECMAScript 標準は現在および将来においても Web の対話機能の中心であると言ってもよいでしょう。

2009年 12月に公開された ES5 は、この言語の漸進的な改善の結果であり、長期間にわたる、そして時には論争に満ちたプロセスを経て完成されています。この仕様の開発にどのような摩擦があったにせよ、最終的に完成した仕様には、素晴らしい機能強化が含まれています。この記事では、この仕様を開発してきた長い歴史の概略を説明し、ES5 で新たに追加された重要な機能と概念の多くについて順を追って説明します。

ECMAScript の歴史

驚くには当たりませんが、この重要性を増しつつある仕様の開発にはさまざまな企業や個人が関係していたため、ES5 への道のりは長く、紆余曲折に満ちていました。以前は、欧州電子計算機工業会 (ECMA: European Computer Manufacturers Association) として知られていた団体は (現在は頭字語ではなく単純に Ecma と記されます)、コンピューター・システムや情報システムに関する国際的な標準化団体です。ECMAScript と密接に関係する DOM 仕様を含め、Ecma で取り扱われた Web 標準の大部分は、W3C の標準にもなっています。しかし JavaScript/ECMAScript は、W3C の標準ではありません。Yahoo! の JavaScript アーキテクトである Douglas Crockford 氏は、JavaScript に関する一連の素晴らしいプレゼンテーションの中で、この状況を明快に説明しています。

1996年当時、JavaScript は Netscape によるブラウザーの技術革新として、とても広く使用されており、Web ブラウザーの領域で追いつこうとしていた Microsoft は、JavaScript をリバース・エンジニアリングし、Windows Internet Explorer ブラウザーの機能として Jscript を発表しました。Crockford 氏は次のように説明しています。

「Netscape は JScript の発表を見て、『これは大変だ。このままでは JavaScript は他の技術に取り込まれて拡張されてしまう。』と危機感を抱き、『それを防ぐには JavaScript を基に標準を作成する必要がある。』と考えました。そこで彼らは W3C を訪ね、『私たちは言語を用意しました。皆さんで標準化してください。』と頼みました。しかし、それまで長い間、W3C は Netscape に対し『地獄に落ちろ』と言う機会を待ち続けていたので、『地獄に落ちろ』と答えました。」
「Netscape は、標準を買うことができる団体を探し求めて ISO やその他の標準化団体を回りました。そして最終的に欧州電子計算機工業会 (ECMA) にたどり着きました。そこはカリフォルニアのソフトウェア企業にとっては遠く離れた場所でしたが、そこが彼らが行き着いた先でした。」

ECMAScript が誕生するまでには、このように標準化団体を求めて彷徨ったという歴史があったにせよ、ECMAScript として標準化されたことで Web 開発コミュニティー全体に大きく貢献するという良い結果がもたらされました。ただし、Netscape 自体はあまりうまく行きませんでした。

第 5 版への道

ES5 が公開される前、ECMAScript の最後のメジャー・バージョンは 1999年 12月に公開された第 3 版でした。第 3 版の公開後、元々 ES4 となるはずだったエディションの作業が開始されました。2003年に中間レポートが発表され、それを基に Adobe と Microsoft が実装を行いました。しかし、中心となる標準化プロセスは、TG1 (Task Group 1 of Ecma Technical Committee 39) のミーティングが定期的に開催されるようになった 2005年秋まで停止していました。

後になってみると、この時が標準化プロセスを再開する好機でした。その頃、Ajax (Asynchronous JavaScript + XML) により、JavaScript が本格的に復興し始めていたからです。この年の 2月には Jesse James Garrett 氏が、強い影響力のある記事「Ajax: A New Approach to Web Applications」(「参考文献」を参照) を発表しており、現在では当然とみなされている技術的な設計パラダイムがすさまじいペースで生まれつつありました。

ES3 (2003年に発表された中間レポート)、および Adobe と Microsoft による実装から学んだ教訓を基に、TG1 はこの言語を大幅に変更する方向に舵を切りました。この記事では TG1 が提案した仕様の詳細については触れませんが、この仕様の持つ野心的な目標は、TG1 のワーキング・グループ内でも論争を引き起こしました。

2007年に、この内部での論争、つまりこの仕様の本質的な目標にかかわる論争が表ざたになりました。誰の目にも、Microsoft の Chris Wilson 氏と、JavaScript 言語の元々の設計者である Mozilla の Brendan Eich 氏の間の意見の相違は明白でした。この 2 人は、この仕様では何を目指すべきかについて対立する意見を持っていました。

Eich 氏は、当時 ES4 として提案されていた、この言語の大幅な機能強化を支持する人々の代表でした。一方、Wilson 氏は、そうした変更によって「Web が破壊される」と感じる人々の代表でした。それらの変更は、既存の仕様からあまりにも大きく変更されるものであったからです。その当時、ES4 には後方互換性がなく、What-WG が HTML5 仕様の設計を決定する際に、この懸念を反映したほどです。

この相違により、標準化グループは実質的に 2 つに分裂し、ES4 に反対する人々は (Microsoft と Yahoo! の主導で) 別の道を歩み、ECMAScript 3.1の作業を開始しました。ECMAScript 3.1 は、漸進的な改善とバグの修正を目標に設計された仕様です。その一方、Eich 氏や ES4 の他の支持者は相変わらず ES4 仕様の作業を続けました。しかし、分裂によるマイナスの影響を考慮し、また Web の将来に対して ECMAScript の重要性が高まるはずという認識から、2 つの標準化グループは両者の違いを調整するための会合を 2008年 7月に開催しました (これは「Oslo Meeting」と呼ばれています)。私たちにとっては幸いなことに、新しい共通の目的を持った TG1 がこの会合から誕生しました。

ES5 と Harmony

Eich 氏が各グループのメーリング・リストで発表した計画は、第 3.1 版の仕様に対する直近の作業に焦点を絞り、その後、(少なくとも ES4 と比較すると) 後退してはいるものの、それでも相変わらず野心的な、ES-Harmony と呼ばれる言語の再構築作業に移るというものでした。この第 3.1 版が、やがて ES5 になりました。

これで ES5 仕様の歴史を理解できたので、次にそうした困難な作業と時々発生した論争によって何が生み出されたのかを見てみましょう。

ES5 で新たに導入された機能と概念

この標準の最新版では、いくつかの領域が注目されています。ES5 には 2 つの重要な領域があり、小規模ながら有用な変更と追加がいくつか行われています。この記事では、ES5 の新機能をサポートするコードのサンプルを紹介します。これらのサンプルを試すためには、ほとんどの場合、Internet Explorer バージョン 9、Mozilla Firefox バージョン 4、または Google Chrome バージョン 6 以降が実行されていなければなりません。また、コンソールも開いておく必要があります。

注: この記事の執筆時点で、Strict モードをサポートするブラウザーは Firefox 4 のみです。最新の互換性情報については、ES5 の互換性一覧表を調べてください (「参考文献」のリンクを参照)。

Strict モード

この新しい仕様の興味深い点の 1 つは、Strict モードが導入されたことです。Strict モードというのは、制限付きの、しかし曖昧さがない JavaScript 言語のサブセットを選択して使用できるメカニズムです。Doug Crockford 氏の精神に立ち返り、Strict モードは JavaScript 言語の「問題のある部分」のうち最もひどいいくつかの点を修正または制限するように設計されています。

Strict モードはスクリプト・レベルまたは関数レベルで実装することができます。これは便利です。モジュールやライブラリーの作成者が厳密なコードを作成することを選択しても、環境の他の部分には影響を与えないからです。1 つ頭に入れておく必要のある点は、パフォーマンスを高める目的で厳密なスクリプトと厳密でないスクリプトとを連結すると、意図せぬ結果が発生する場合があることです。一例として、「参考文献」に挙げた Amazon のバグを調べてみてください。

スクリプト・レベルで Strict モードを呼び出すためには、どの文よりも前にファイル内に use strict 文を含めるようにします。リスト 1 にその例を示します。

リスト 1. スクリプト・レベルで Strict モードを呼び出す
"use strict";
var myNamespace = {
    myMethod : function(){
    	//strict code
	}
}

同様に、関数レベルで Strict モードを呼び出す場合にも、どの文よりも前に関数本体の中に use strict 文を含めるようにします。リスト 2 にその例を示します。

リスト 2. 関数レベルで Strict モードを呼び出す
function getStrict(){
	"use strict";
	//strict code
}

Strict モードを呼び出すために使用される構文が、単なる文字列式の文なのは興味深い点です。これはつまり、この文をコードの中に含めたとしても、何も害はないということです。この文を認識できるブラウザーは、Strict モードのサブセットを起動します。一方、認識できないブラウザーは、この文を文字列式の文として読み取り、その結果としてこの文を無視して次のコード行に進みます。もちろん、Strict モードが一部のブラウザーでしかサポートされていない場合に、Strict モードを呼び出すことには他の懸念もありますが、use strict ディレクティブそのものには、そうした懸念はありません。

Strict モードの実際

これ以降のセクションでは、Strict モードがコードに与える影響を概説します。ここでは Strict モードのすべての側面を余すところなく検証するわけではありませんが、この新しいモードの主要なテーマを説明していきます。Strict モードを理解する最善の方法は、この記事で取り上げる例を、Strict モードをサポートするブラウザーで試してみることです。

また勇気がある人なら、既存のスクリプトの先頭に use strict ディレクティブを追加し、いくつエラーが発生するかを調べるという方法もあります (あるいは十分注意して、以下で説明する落とし穴を避ければ、エラーは発生しないかもしれません)。

厳密にスローされるエラーと、不注意によるグローバル変数の作成の防止

私が Strict モードで特に気に入った部分は、コードのあら探しができることです。Strict モードでは、よくある不適切なコーディング・パターンが実際にエラーになります。これらのパターンは、標準的なスクリプトの中では、通常は無視されるか、あるいは暗黙のうちに失敗します。そうした不適切なコーディング・パターンの中で最も一般的なのは、誤ってグローバル変数を作成してしまうことです。これは var 文を使用していない、つまり宣言されていない変数に値を割り当てることによって起こります。標準モードでは、このパターンは単に悪いプラクティスにすぎませんが、Strict モードでは、このパターンによって参照エラーがスローされます (リスト 3)。

リスト 3. 不注意によるグローバル変数の作成の防止
function nopullution(){
	"use strict";
	myAccidentalGlobal = "polluting the global namespace is for losers";
}
nopullution();
>>>Error: assignment to undeclared variable myAccidentalGlobal

不注意によるグローバル変数の作成を防止する別の方法としては、特定の状況で this キーワードのバインド方法を変更する方法もあります。例えば、コンストラクター関数を呼び出す際に、新しい接頭辞を使用せずに直接呼び出す場合、this はグローバル・オブジェクトにバインドされます。しかし、Strict モードではこの場合、thisundefined にバインドされるため、誤ってグローバル名前空間が汚染されることはありません。

また、通常であれば単純に暗黙のうちに失敗するアクション (削除できないプロパティーを削除する、引数やプロパティーの名前に同じ名前を使用する、など) の場合、Strict モードではエラーがスローされます。これらの例をリスト 4 に示します。

リスト 4. 厳密にスローされるエラー
"use strict"
delete Array.prototype;

>>>Error: property "use strict";Array.prototype is non-configurable and
can't be deleted
"use strict"
var myObj ={
        dupe: 1,
        dupe :2
}
>>>Error: property name dupe appears more than once in object literal

静的バインディング

また Strict モードでは、動的バインディングを利用する言語機能を使用できなくすることで、変数名の静的バインディングが強制的に行われます。これは呼び出しコンテキストで変数を作成する eval の機能を制限して、with 文を使用できないようにすることで実現されます。Strict モードでは、eval はサンドボックスの中で実行され、そのサンドボックスは eval が終了すると破棄されます。これによる利点の 1 つは、JavaScript 縮小化ツールの圧縮効率が向上する点です。というのは、静的バインディングに関する部分を縮小化ツールで最適化できるからです。

Object の機能強化

興味深い領域の 2 番目は、コアとなる Object に対して強く望まれていた一連の機能強化についてです。いくつかの新機能によって Object が強化され、ECMAScript の継承機能に対して、必要とされていた強力な機能が追加されています。

Object.create()

Object.create メソッドを使用すると、指定されたプロトタイプ・オブジェクトと追加のプロパティー・オブジェクトを使用して新しいオブジェクトを作成することができます。これはオブジェクトを作成、継承、再利用するための新しい強力なインターフェースです。create() を使用すると、継承を設定することや、オブジェクトの機能を強化することができます。しかもそれらすべてを、明確に定義された 1 つのインターフェースで行うことができます。Object のプロパティーによって実現される新機能が利用できるようになることで、JavaScript による継承は従来よりもはるかに簡単な作業になりました。リスト 5 に単純な例を示します。

リスト 5. Object.create() を使用する
//create a simple, traditional object that has some properties
var marvelProto = {
    publisher : "Marvel Comics",
    founded : "1939",
    founder : "Martin Goodman",
    headquarters : "417 5th Avenue, New York, NY, U.S."
}

//create a new object that inherits properties from the simple object
//create() takes two arguments. the object that will serve
//as the new object's prototype and an optional set of 
//new properties to populate the new object 
var FantasticFour = Object.create(
    marvelProto,
    {
        title : {
            value : "Fantastic Four",
            
        },
        year : {
            value : "1961",
            
        }
    }
);
console.log(
    FantasticFour.title 
    +" was first published by "+
    FantasticFour.publisher 
    +" in "+ 
    FantasticFour.year
);

プロパティーの属性

create() の機能強化として一連の新しいプロパティーの属性が導入され、これらの属性によって、従来よりもはるかに詳細に継承を制御することができるようになりました。その一例として、先ほどの例では明示的なラベルである value を使用しています。他にも 3 つのブール値のフラグを使用することで、以下のように Object の柔軟性を非常に高めることや、Object を詳細に制御することができます。

  • 書き込み可能なプロパティーの値を変更することができます。
  • enumerable により、for...in ループからプロパティーが見えるかどうか、また新しい Object.keys() メソッドの中でプロパティーが見えるかどうかを指定することができます。
  • 構成可能なプロパティーを削除したり、それらのプロパティーの属性を変更したりすることができます。

先ほどの例は新しいプロパティーの属性によって改善することができます。つまり、定義された後は変更されるべきではない属性を、新しいプロパティーの属性によってロックダウンします。この新しいバージョンをリスト 6 に示します。

リスト 6. プロパティーの属性を使用してオブジェクトを定義する
var marvelProto = Object.create(
    //pass in the default object as a template
    {},
    {
    //and then create more finely defined properties    
        publisher : {
            value  : "Marvel Comics",
          //yes, we want it exposed
            enumerable: true,
          //Theoretically, the value might change  
            writable: true,
          //but it can't be deleted/edited  
            configurable: false
        },
        founded : {
            value : "1939",
            enumerable: true,
                //this won't change, so we stop it from being overwritten
            writable: false,
            configurable: false
        },
        founder : {
            value : "Martin Goodman",
            enumerable: true,
            writable: false,
            configurable: false
        },
        headquarters : {
            value : "417 5th Avenue, New York, NY, U.S.",
            enumerable: true,
            //we might need to edit at some point
            writable: true,
            configurable: false
        }
    }
);
var FantasticFour = Object.create(
    marvelProto,
    {
        title : {
            value : "Fantastic Four",
            enumerable: true,
            writable: false,
            configurable: false
        },
        year : {
            value : "1961",
            enumerable: true,
            writable: false,
            configurable: false
        }
    }
);
console.log(
    FantasticFour.title 
    +" was first published by "+
    FantasticFour.publisher 
    +" in "+ 
    FantasticFour.year
);

ロックダウン・メソッド

Object のきめ細かい調整や制御を可能にするというトレンドが続いている中、ES5 では新たに 3 つのメソッドが作成されています。これらのメソッドを使用すると、1 つのオブジェクトに対し、異なるレベルの「ロックダウン」を適用することができます。この切望されていた強力なツールにより、ECMAScript プログラムの動作が大幅に予測しやすくなります。

注: この記事で紹介する例は、いずれも Strict モードを使用しており、ロックダウン・メソッドによって禁止されているアクションを実行しようとすると強制的にエラーがスローされます。

Object.preventExtensions() と Object.isExtensible()

最初に説明するロックダウン・メソッドは、Object.preventExtensions() です。このメソッドにより、新しい名前付きプロパティーがオブジェクトに追加されるのを防ぐことができます。Object.isExtensible() は、名前付きプロパティーをオブジェクトに追加できるかどうかをテストします。この 2 つのメソッドをリスト 7 で説明します。

リスト 7. Object.preventExtensions() と Object.isExtensible() を使用する
"use strict" 
var marvelProto = Object.create(
    //pass in the default object as a template
    {},
    {
        publisher : {
            value  : "Marvel Comics",
            enumerable: true,
            writable: false,
            configurable: false
        }
       /*...OTHER PROPERTIES*/
    }
);
//Let's see if we can add some more info to the object
if (Object.isExtensible(marvelProto)){
    //true
    marvelProto.flagshipCharater = "Spider-Man";
};

//Stop extensions
Object.preventExtensions(marvelProto);

//Adding an Editor in Chief field throws an error
marvelProto.editorInChief = "Axel Alonso";
>>>TypeError: Can't add property editorInChief, object is not extensible

Object.seal() と Object.isSealed()

Object.seal は最初に Object.preventExtensions() を呼び出し、新しいプロパティーが追加されるのを防ぎ、次にすべてのオブジェクト・プロパティーの configurable フラグを false に設定します。これにより、オブジェクトはオブジェクトの値以外はロックされるため、データ・ストアとして使用する場合の動作を予測できるようになります。Object.seal() が実行されたデータ・オブジェクトの値をループ処理する動作は、非常に予測しやすいものとなります。特に enumerable フラグと組み合わせると動作がより予測しやすくなります。Object.isSealed() はオブジェクトが構成可能かどうかをテストします。リスト 8 に、この 2 つのメソッドの使用例を示します。

リスト 8. Object.seal() と Object.isSealed() を使用する
"use strict" 
var marvelProto = Object.create(
    //pass in the default object as a template
    {},
    {
        publisher : {
            value  : "Marvel Comics",
            enumerable: true,
            writable: false,
            configurable: false
        },
       /*...OTHER PROPERTIES*/
        //optional properties
        flagshipCharacter : {
            value  : "Spider-Man",
            enumerable: true,
            writable: false,
            configurable: true
        },
        editorInChief : {
            value  : "Axel Alonso",
            enumerable: true,
            writable: false,
            configurable: true
        }
    }
);

//Can we delete configurable properties?
if (!Object.isSealed(marvelProto)){
    //we can
    delete marvelProto.flagshipCharacter;
};

//Seal the object
Object.seal(marvelProto);

//deleting the Editor in Chief field throws an error
delete marvelProto.editorInChief;
>>>Error: property "use strict";marvelProto.editorInChief is 
		non-configurable and can't be deleted

Object.freeze() と Object.isFrozen()

Object.freezeObject.seal() を呼び出してオブジェクトを構成できないようにし、続いてすべてのオブジェクト・プロパティーの writeable フラグを false に設定し、完全に静的なオブジェクトにします。複数のソースがデータを操作する環境では、完全にオブジェクトをフリーズする機能により、従来はまったく不可能であった一定レベルの予測性と安全性を確保することができるようになります。

Object.isFrozen() はオブジェクトがフリーズされているかどうかをテストします。リスト 9 に、この 2 つのメソッドの使用例を示します。

リスト 9. Object.freeze() と Object.isFrozen() を使用する
"use strict" 
var marvelProto = Object.create(
    //pass in the default object as a template
    {},
    {
        publisher : {
            value  : "Marvel Comics",
            enumerable: true,
            writable: false,
            configurable: false
        },
        //optional properties
        flagshipCharacter : {
            value  : "Spider-Man",
            enumerable: true,
            writable: false,
            configurable: true
        }
    }
);

//Can we write writeable properties?
if (!Object.isFrozen(marvelProto)){
    //we can
    marvelProto.flagshipCharacter = "Iron Man";
};

//Seal the object
Object.freeze(marvelProto);

//Changing the Flagship Character throws an error
marvelProto.flagshipCharacter = "Wolverine";
>>>Error: "use strict";marvelProto.flagshipCharacter is read-only

Object.getPrototypeOf()

新しいメソッド Object.getPrototypeOf() はオブジェクトのプロトタイプを返します。このメソッドの値は非標準の Object.__proto__ プロパティーと等価です。

Object.keys() と Object.getOwnPropertyNames()

Object.keys() メソッドは、オブジェクトが持つ列挙可能なプロパティーの名前を表すストリングの配列を返します。列挙可能なプロパティーとは、対象オブジェクトに直接定義されたプロパティーのうち、enumerable フラグが true に設定されたプロパティーという意味です。Object.getOwnPropertyNames() も似ていますが、このメソッドは enumerable フラグが false に設定されたプロパティーも含めて返します。この両方のメソッドをリスト 10 に示します。

リスト 10. Object.keys() と Object.getOwnPropertyNames() を使用する
var USComicPublishers = {
    countryOfOrigin : {
        value  : "USA",
        enumerable: true,
        writable: false,
        configurable: false
    },
    medium : {
        value  : "comic books",
        enumerable: true,
        writable: false,
        configurable: false
    }
}
var marvelProto = Object.create(
//inherits properties from another Object
    USComicPublishers,
    {
        publisher : {
            value  : "Marvel Comics",
            enumerable: true,
            writable: false,
            configurable: false
        },
        founded : {
            value : "1939",
            enumerable: true,
            writable: false,
            configurable: false
        },
        founder : {
            value : "Martin Goodman",
            enumerable: true,
            writable: false,
            configurable: false
        },
        headquarters : {
            value : "417 5th Avenue, New York, NY, U.S.",
            enumerable: true,
            writable: true,
            configurable: false
        },
        launchComic : {
            value : function(){
                /*fancy code to create a new comic series
                this isn't data we want to expose when we inspect
                inherited objects */
                return true;
            },
            enumerable: false,
            writable: true,
            configurable: false
        }
    }
);

//inherits medium from parent, as expected
console.log(marvelProto.medium.value);
>>>comic books

//Our function is available
console.log(marvelProto.launchComic());
>>>true

//BUT... keys returns ONLY properties on object itself
//inherited properties are ignored
//launchComic (enumerable:false) is also skipped
console.log(Object.keys(marvelProto));
>>>["publisher", "founded", "founder", "headquarters"]

//getOwnPropertyNames also operates only on 
//properties of the object itself, but it
//also includes properties that have the
//enumerable flag set to false 
console.log(Object.getOwnPropertyNames(marvelProto);
>>>["launchComic", "founder", "founded", "headquarters", "publisher"]

アクセサー (ゲッターとセッター)

getset は、プロパティーの値にアクセスまたは書き込みしようとした場合に呼び出される関数にオブジェクト・プロパティーをバインドします。get は引数をとらず、set は引数 (設定する値) を 1 つとります。この両方の要素をリスト 11 で説明します。

リスト 11. ECMAScript のゲッターとセッター
var marvelProto = Object.create(
    //pass in the default object as a template
    {},
    {
        publisher : {
            value  : "Marvel Comics",
            enumerable: true,
            writable: false,
            configurable: false
        },
   /*OTHER PROPERTIES*/
    }
);
    
var FantasticFour = Object.create(
    marvelProto,
    {
        title : {
            value : "Fantastic Four",
            
        },
        year : {
            value : "1961",
            
        }
    }
);

//Use Object.defineProperty to set the getters and setters
//Alternatively, this could be set in the Object.create above
Object.defineProperty(FantasticFour, "bestIssue", {
    get: function () { return fave; },
    set: function (num) {
            fave = "The best single issue of Fantastic Four is issue #" + num;
        }
    }
);

FantasticFour.bestIssue = 51;
console.log(FantasticFour.bestIssue);
>>>The best single issue of Fantastic Four is #51

Array メソッド

この新しい仕様で非常に注目されている、もう 1 つの領域が配列です。配列に関する変更は Object に対する機能強化ほど根本的なものでも強力でもありませんが、注目に値する新しいメソッドがたくさんあります。

Array.forEach()

Array.forEach 関数は引数を 1 つとります。この引数は、配列の各要素に対して 1 度だけ実行される関数です。実行される関数には、アクセス対象の特定の要素、その要素のインデックス、およびその配列、という 3 つの引数が渡されます。リスト 12 に、このコンビニエンス・メソッドの簡単な使用法を示します。

注: このメソッドの実行には、同じ結果を得られる for ループよりも大幅に時間がかかります。これは要素ごとに別の関数が実行されるというオーバーヘッドによるものです。各ステップで、新しい実行コンテキストが作成されて破棄され、別のレベルがスコープ・チェーンに追加されます。これらが累積されることで実行時間が長くなります。

リスト 12. Array.forEach() の例
 var arr = [8, 10, 13, 10, 8, 1, 5];
function logger(element, index, array) {
    console.log("The value of the element at index " + index + " is " + element); 
}
arr.forEach(logger);
>>>The value of the element at index 0 is 8
>>>The value of the element at index 1 is 10
>>>The value of the element at index 2 is 13
>>>The value of the element at index 3 is 10
>>>The value of the element at index 4 is 8
>>>The value of the element at index 5 is 1
>>>The value of the element at index 6 is 5

Array.map()

Array.map は、配列の各要素に対し、引数として提供される 1 つの関数を呼び出すことで生成される新しい配列を返します。提供される関数は、配列の要素を 1 つの引数として受け取ります。リスト 13 に簡単な例を示します。

リスト 13. Array.map() の例
 var arr = [8, 10, 13, 10, 8, 1, 5];
function square(num){
    return num * num;    
}    
console.log(arr.map(square));  
>>>[64, 100, 169, 100, 64, 1, 25]

Array.reduce() と Array.reduceRight()

Array.reduce()Array.reduceRight() はどちらも、提供される関数を配列の 2 つの要素に対して実行し、それを配列全体に繰り返すことによって 1 つの値を生成します。Array.reduce() はそれを左から右に実行し、Array.reduceRight() は配列を右から左へと処理します。順序の重要性を示す例を含む両方の例をリスト 14 に示します。

リスト 14. Array.reduce() と Array.reduceRight() を使用する
var arr = [8, 10, 13, 10, 8, 1, 5];

console.log(arr.reduce(function(a, b){ return  a + b; }));           
>>>55

console.log(arr.reduce(function(a, b){ return  a +" "+ b; }));               
>>>8 10 13 10 8 1 5

console.log(arr.reduceRight(function(a, b){ return  a + b; }));           
>>>55

console.log(arr.reduceRight(function(a, b){ return  a +" "+ b; }));
>>>5 1 8 10 13 10 8

Array.filter()

Array.filter() 関数は、引数として提供される 1 つの関数によって実施されるテストをパスする要素をすべて含む、新しい配列を返します。単純な偶数/奇数テストを使用した例をリスト 15 に示します。

リスト 15. Array.filter() を使用する
var arr = [8, 10, 13, 10, 8, 1, 5];
function odd(element, index, array) {
     return (element%2);
}
console.log(arr.filter(odd));
>>>[13, 1, 5]

Array.every() と Array.some()

Array.every()Array.some() はどちらも、配列の中の要素を、引数として提供される 1 つの関数に対してテストします。Array.every() は、提供される関数によって実施されるテストに配列要素がすべてパスした場合、true を返します。Array.some() は、提供される関数によって実施されるテストに配列の要素が 1 つでもパスした場合、true を返します。単純な偶数/奇数テストを使用した両方の例をリスト 16 に示します。

リスト 16. Array.every() と Array.some() を使用する
var arr = [8, 10, 13, 10, 8, 1, 5];
function odd(element, index, array) {
     return (element%2);
}
console.log(arr.every(odd));
>>>false

console.log([1,3,5].every(odd))
>>>true

console.log(arr.some(odd));
>>>true

console.log([2,4,6].some(odd))
>>>false

Array.indexOf() と Array.lastIndexOf()

Array.indexOf() 関数と Array.lastIndexOf() 関数はどちらも、引数として指定される要素が、配列の中で検出される場所のインデックスを返します。あるいは、その要素が存在しない場合には -1 を返します。Array.indexOf は配列の最初の要素から、またはオプションである位置の引数で指定される要素から検索を開始し、配列の末尾に至るまで一致する要素を検索します。Array.lastIndexOf() は配列の最後の要素から、あるいはオプションである位置の引数で指定される要素から検索を開始し、配列の先頭に至るまで一致する要素を検索します。それぞれの簡単な例をリスト 17 に示します。

リスト 17. Array.indexOf() と Array.lastIndexOf()
 var arr = [8, 10, 13, 10, 8, 1, 5];
 
console.log("lastIndexOF is " + arr.lastIndexOf(10));
>>>lastIndexOF is 3

console.log("indexOF is " + arr.indexOf(10));
>>>indexOF is 1

JSON

JSON は、人気の高いデータ交換フォーマットであり、JavaScript 構文のサブセットをベースとしています。ES5 では、json2.js リファレンス実装と互換性がある形で JSON を標準化しています。ES5 には JSON.parse()JSON.stringify() という 2 つのメソッドがあります。

JSON.parse()

JSON.parse() メソッドは JSON テキストのストリングを引数に取り、そのストリングが有効であればオブジェクトまたは配列に変換します。リスト 18 は JSON.parse() の簡単な使用法を示しており、ここではストリングをオブジェクトに変換しなおしています。

リスト 18. JSON ストリングを JSON.parse() によって構文解析する
var marvel = JSON.parse('{"publisher":"Marvel Comics", /*other properties*/}');
console.log(marvel.founder);
>>>Martin Goodman

JSON.stringify()

JSON.stringiy() メソッドはオブジェクトを JSON ストリングに変換します。リスト 19 ではサンプルのオブジェクトをストリングに変換しています。

リスト 19. JSON.stringify() を使用してオブジェクトをストリングに変換する
var marvelProto = Object.create(
    {},
    {
        publisher : {
            value  : "Marvel Comics",
            enumerable: true,
            writable: false,
            configurable: false
        }
        /*OTHER PROPERTIES*/
    }
);
console.log(JSON.stringify(marvelProto));
{"publisher":"Marvel Comics","founded":"1939","founder":"Martin Goodman"}

その他の興味深い新機能

その他にも注目に値する新機能がいくつかあるので、最後にそれらを紹介します。

Date.now()

Date.now() メソッドは現在の時刻を取得するための新しい便利なメソッドです。このメソッドは一般的な (new Date()).getTime() パターンを置き換えるものです。リスト 20 に Date.now() の例を示します。この例では、500 ミリ秒を setTimeout に引数として渡した時点の時刻と、その待機時間後に later() 関数が再実行される時点の時刻との差をテストしています。

リスト 20. Date.now() を使用する
var datePrev = Date.now(),
        dateNow,
        count=0;
function later(){
        if (count < 5 ){
            dateNow = Date.now();
            console.log(datePrev - dateNow);
            datePrev=dateNow;
            setTimeout(later,500);
            count++;    
        }
}
later();

>>>0
>>>-487
>>>-500
>>>-512
>>>-500

Function.prototype.bind()

簡単に言えば、Function.prototype.bind() は Prototype.js の革新的なメソッドとしてよく使われており、指定された this の値を使用して後で実行される関数を定義します。また引数を指定して関数にバインドできるため、バインドされた関数をカスタマイズすることもできます。この強力な機能の基本的な例をリスト 21 に示します。

リスト 21. Function.prototype.bind() の簡単な例
//global values;
var favoriteIssue = 27,
        title = "Detective Comics";
var myFavoriteComic = {
        returnFavorite: function() {
                    if (arguments.length && arguments[0].newComic) {
                                this.favoriteIssue =arguments[0].newComic.favoriteIssue
                               this.title =arguments[0].newComic.title
                        };
        //what's this?
            console.log("this is "+this);
            //display the values
console.log("My favorite issue of "+ this.title +" is #" + this.favoriteIssue);
        },
        favoriteIssue: 168,
        title : "Daredevil"
};

//this is myFavoriteComic
//values are internal to the module
myFavoriteComic.returnFavorite();
>>>this is [object Object]
>>>My favorite issue of Daredevil is #168

//now we let this slip to the global object
var makeGlobal = myFavoriteComic.returnFavorite;
makeGlobal();
>>>this is [object Window]
>>>My favorite issue of Detective Comics is #27

//use bind to bind it to the correct this value
var boundToModule = makeGlobal.bind(myFavoriteComic);
boundToModule();
>>>this is [object Object]
>>>My favorite issue of Daredevil is #168

//pass in options to change the module values
var updatedFavoriteIssue = makeGlobal.bind(
myFavoriteComic,
{
	newComic :
		{        
			favoriteIssue:51, 
			title:"Fantastic Four"
		}

}
);
updatedFavoriteIssue()
>>>this is [object Object]
>>>My favorite issue of Fantastic Four is #51

まとめ

この記事をまとめる上で、ライブラリーに着想を得たメソッドを最後に説明したのは適切だったようです。なぜなら、ライブラリー作成者やその他の中核的な JavaScript 開発者こそが、この仕様の新しい機能や関数の真の実力を明らかにする人々だからです。ES5 をサポートするブラウザーはますます増えており、またこの仕様の最新版を詳細に調べ上げ、この仕様を基にして作業を開始する開発者も増えていることから、皆さんもこれらの新しいツールが持つ可能性を実感し始めることでしょう。誕生後 10 年が経ったこの仕様を基にブラウザーのベンダーや開発者が行ってきた成果を考えると、この最新の改訂版によって、向こう数年は Web にとってより活発な動きのある年になりそうです。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=659386
ArticleTitle=ECMA-262 第 5 版
publish-date=04122011