- ベストアンサー
戻り値のリテラル関数の引数の出所はどこなの?
お世話になっております。現在JavaScriptの勉強をしております。 その一環としてPrototype.js ver 1.6.1 を解読しているのですが Enumerable 等のメソッドで出てくる return でリテラル関数を指定するような部分が多々見受けられるのですが…この意味がイマイチ感覚がつかめず困っています。 例:Prototype.js 702行目付近 Enumerable = (function(){... の collectメソッド(743行目付近) function collect(iterator, context) { iterator = iterator || Prototype.K; var results = []; this.each(function(value, index) { results.push(iterator.call(context, value, index)); }); return results; } などの this.each(function(value, index) { results.push(iterator.call(context, value, index)); } 部分で function(value, index)... とあるvalue, index はどこでどれから指定されるのかがよくわかりません。 [1,2,3,4].collect(a,b); と呼び出した場合、collect(a,b){... と呼ばれますが value と indexには何がどうやって指定されるのでしょうか? このような記法が把握できず、混乱を極めている状態です。 どなたかお手数ですがお教え願えないでしょうか? よろしくお願いいたします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
prototype.jsからコードを抜き出して読み易くまとめてみました。 Arrayオブジェクトしか扱えませんけど。 //- Arrayオブジェクトの拡張 ------------------------- Array.prototype._each = function (iterator) { for (var i = 0, length = this.length; i < length; i++) { iterator(this[i]); // callback関数第一引数 (this[i]) } }; Array.prototype.each = function (iterator, context) { var index = 0; this._each(function(value) { iterator.call(context, value, index++); // callback関数第二引数 (index++) }); } Array.prototype.collect = function (iterator, context) { var results = []; this.each(function(value, index) { results.push(iterator.call(context, value, index)); }); return results; } //- Arrayオブジェクトの拡張ここまで ------------------ var arr = [ 2, 4, 6, 8 ]; //各要素を2倍した値に置き換えその結果を返す var callback = function (v, i) { return this[i] = v * 2; }; var results = arr.collect(callback, arr /* 関数内this値 */); alert('collectメソッドの戻り値: ' + results); alert('arrの値: ' + arr); //- ここまで ----------------------------------------- //Array.collect()に限るなら Array.prototype.collect = function (iterator, context) { var results = []; for (var i = 0, length = this.length; i < length; i++) { results.push(iterator.call(context, this[i], i)); } return results; } Function.prototype.call http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/15-3_Function_Objects.html#section-15.3.4.4 Function.callメソッドさえ抑えれば難しくないはずです。
その他の回答 (4)
- yyr446
- ベストアンサー率65% (870/1330)
No.1さらに気づいた事 iterator.call(context, value, index) の部分は単にiteratorを実行しているのではなく、 iteratorクラスの継承でvalueとindexをiterator内で 使える用にしているという事かなあ。 想像ばかりの回答で、すんません。 有識者の回答を待ちましょう....
お礼
たびたびご回答いただきありがとうございます。 Arrayに関しては_eachを実装しているので分かりやすいので逆にたどればEnumerableとの動作連携は分かる気がします。 のんびり待ちます… その前に自力で把握できればいいのですけどね。(苦笑
- yyr446
- ベストアンサー率65% (870/1330)
No.1です。私の回答ちょっと違ってました。 オブジェクト指向にそんなに詳しいわけじゃないんですが。 「Enumerableでとなっているの所のvalue はどこから来るんだ?」 valueはやはりthisで受け渡されていくような気がします。 Enumerable.collect(iterator)はリストEnumerableを返すのですよね。 ret=[1,2,3,4].collect(function(val,idx){return val+idx;}); だとcollect(){}の中でthisは[1,2,3,4]ですよね このthisが、this.each(function(value, index) { results.push(iterator.call(context, value, index)); }); でeach(){}で処理され、each(){}の中のthisも [1,2,3,4]になります。さらにeachの中で this._each(function(value) { iterator.call(context, value, index++); }); となっているので、this [1,2,3,4] がそのまま_eachで処理されます。 よって_each(){}の中のthisも[1,2,3,4]のことになるので _each: function(iterator) { for (var i = 0, length = this.length; i < length; i++) iterator(this[i]); } iterator(this[i]); で、[1,2,3,4] の i 番目がiteratorで処理されます、 iterator関数内に引き渡す際の引数名が単にvalueなのではないかと、
- yyr446
- ベストアンサー率65% (870/1330)
No.1補足 this.eachのeachもprototype.jsのメンバーで、 var Enumerable = { each: function(iterator, context) { var index = 0; try { this._each(function(value) { iterator.call(context, value, index++); }); } catch (e) { if (e != $break) throw e; } return this; } となっており、さらに別のところで Object.extend(Array.prototype, { _each: function(iterator) { for (var i = 0, length = this.length; i < length; i++) iterator(this[i]); } ますから、indexはこの仕組みの中で作られて、使われるわけですね。
補足
お世話になります。 Object.extend(Array.prototype, { _each: function(iterator) { for (var i = 0, length = this.length; i < length; i++) iterator(this[i]); } のところは感覚として分かるのです。配列インスタンス(this)の各要素にiteratorを実行するのだと… //--- each: function(iterator, context) { var index = 0; try { this._each(function(value) { iterator.call(context, value, index++); }); } catch (e) { //--- Enumerableでとなっているの所のvalue はどこから来るんだ? となってしまうのです。_each(以降の部分がiterator?としてEnumerableを追加されたクラスの_each()へ行くのかな?と なんだか迷走してしまうんです。 お手数ですがお答えいただけたら幸いです。
- yyr446
- ベストアンサー率65% (870/1330)
質問の意味がよくわからないのですが... [1,2,3,4].collect(a,b);と使うのは変です。a,bは何ですか? 列挙型オブジェクト.collect(イテレータ関数); と使われるはずです。 function collect(iterator, context){} の内部で引き渡したイテレータ関数が results.push(iterator.call(context, value, index)); で、iterator()の名前で呼び出されて、結果をresultsに格納して return results;で返しています。 iterator.call(context, value, index)時の valueとindexは、 this.each(function(value, index) { } 内にありますから、 thisのそれぞれの配列要素と要素キーです。 thisはcollect()のを呼んだやつの列挙型オブジェクトです。 つまり [1,2,3,4].collect(function(a,b){alert(a[b])}); ならaがvalueで[1,2,3,4]、bがindexでその配列の 要素キー 0,1,2,3 をbでアクセス出来る という事では....
補足
お世話になっております。 言葉足らずで申し訳ありません。collect()自身に疑問があるわけではなく、 >iterator.call(context, value, index)時の >valueとindexは、 >this.each(function(value, index) { >} >内にありますから、 >thisのそれぞれの配列要素と要素キーです。 >thisはcollect()のを呼んだやつの列挙型オブジェクトです。 >つまり >[1,2,3,4].collect(function(a,b){alert(a[b])}); >ならaがvalueで[1,2,3,4]、bがindexでその配列の >要素キー 0,1,2,3 をbでアクセス出来る >という事では.... とご回答いただいたわけですが、まさにそこが疑問点なのです。 [1,2,3,4].collect(function(a,b){alert(a[b])}); ならaがvalueで[1,2,3,4]、bがindexでその配列の 要素キー 0,1,2,3 をbでアクセス出来る のaがなぜvalueの[1,2,3,4]になり、bがindexになるのか? というところが分からないのです。 「そうなるんだよ」と言われたらそれまでなのですが、 なぜそうなるのかおわかりでしたらお教え願えないでしょうか?
補足
ご回答いただき誠にありがとうございます。 callback変数に関数オブジェクトを代入することで ずいぶん感覚がつかみやすくなりました。 何となくわかってきた気がします。 要はEnumerable の定義でのfunction(value, index)は 「Enumerable をインタフェースに持つクラスはそのメソッド使う時は こういうフォーマットの評価・処理関数をしてくださいな」 ということなのでしょうか? >var arr = [ 2, 4, 6, 8 ]; >//各要素を2倍した値に置き換えその結果を返す >var callback = function (v, i) { return this[i] = v * 2; }; >var results = arr.collect(callback, arr /* 関数内this値 */); ↓ >Array.prototype.collect = function (iterator, context) { >var results = []; >this.each(function(value, index) { >results.push(iterator.call(context, value, index)); >}); ↑のthis.each(fuction(v, i){results.puch(callback.call(context, v, i);} となるイメージでよろしいのでしょうか?