クロージャ
クロージャとはプログラミング言語において、自身が定義された環境を参照する実行可能なコード片のこと
クロージャとは
outerは高階関数で、関数innerを返します。
function outer(fuga) { var hoge = 4; function inner(piyo) { return hoge * fuga * piyo; } return inner; }
通常、ローカル変数hoge、fugaは関数outerが終了した時点で破棄されますが、outerの戻り値inner関数がローカル変数hoge、fugaを参照し続けるため、破棄されません。
var inner = outer(3); print(inner(2)); // 24
このようにローカル変数を取り込んだ関数をクロージャと言います。
コレクションとクロージャ
クロージャはコレクションオブジェクトとの相性が非常に良い機能です。Rubyのeachメソッドのようにコレクションオブジェクトの各要素に対して反復処理を行い、処理内容をクロージャで定義すると簡潔に記述できます。
# Rubyのイテレータ someArray.each do |enum| p enum end
関数を関数に引数にできる性質を使って、JavaScriptにRubyの内部イテレータに近い処理を実装することができます。
Firefox 1.5では関数オブジェクトを引数として繰り返し処理を行うArrayメソッドがネイティブで追加されています。
eachメソッド
配列要素すべてに対して、何らかの処理を加えます。
Array.prototype.each = function(func){ for (var i = 0; i < this.length; i++) { func(this[i]); } } var someArray = new Array("nullre","hoge", "xxx", "moge", "nullpo"); someArray.each( function(e){print(e);} );
配列要素すべてが標準出力に書き出されます。
selectメソッド
Rubyのselectメソッド(コレクションの中から、ある条件に合う要素を探し出し、それら要素を新しい配列として返すメソッド)を独自実装します。
Array.prototype.select = function(func){ var a = new Array(); for(var i = 0; i < this.length; i++) { if( func(this[i]) ){ a.push(this[i]); } } return a; } var selectedArray = someArray.select( function(e){return e.length == 4} );
文字列の長さが4である要素のみを取り出した新しい配列が作成されます。
collectメソッド
collectメソッド(コレクションのすべての要素にある処理を実行して、その戻り値を要素とする新しい配列を作成するメソッド)を独自実装します。
Array.prototype.collect = function(func){ var a = new Array(); for (var i = 0; i < this.length; i++) { a.push( func(this[i]) ); } return a; } var collectedArray = someArray.collect( function(e){return e.charAt(0); } );
配列の各要素の最初の一文字を要素とする新しい配列が作成されます。
hasOwnProperty
Arrayのインスタンスに対してfor inループを使うと、Arrayの全要素を列挙できますが、Array.prototypeに対してメソッドが追加されていると、それらの追加メソッドまで列挙されてしまいます。このような場合は、hasOwnPropertyメソッドを使うと、Array.prototypeに追加したメソッドを除外できます。
hasOwnPropertyメソッドは指定のプロパティが、オブジェクト自身に属しているならばtrueを返し、prototypeに追加したものはfalseを返します。指定のプロパティが無い場合も当然falseを返します。
for (var item in someArray) { if (someArray.hasOwnProperty(item)) { document.write(someArray[item]); } }
MacOSX 10.3のsafariにはhasOwnPropertyが実装されていませんが、独自にメソッドを追加して対応することができます。10.4以降のSafariでは必要ありません。
/* for safari */ if (!Object.hasOwnProperty) { Object.prototype.hasOwnProperty = function(it) { return (this[it] && !this.constructor.prototype[it]); } }