javascript forEachメソッドにはどのような用途がありますか(そのマップではできません)?


90

mapとforeachで見られる唯一の違いmapは、配列を返すことと返さforEachないことです。ただし、forEachメソッド " func.call(scope, this[i], i, this);"の最後の行もわかりません。たとえば、「this」と「scope」は同じオブジェクトthis[i]i参照しておらず、ループ内の現在の値を参照していないのですか?

別の投稿で誰かが「forEachリストの各要素に基づいて何かを実行したいときに使用します。たとえば、ページに何かを追加している可能性があります。基本的には、「副作用」が欲しいときに最適です。副作用の意味がわかりません。

Array.prototype.map = function(fnc) {
    var a = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
        a[i] = fnc(this[i]);
    }
    return a;
}

Array.prototype.forEach = function(func, scope) { 
    scope = scope || this; 
    for (var i = 0, l = this.length; i < l; i++) {
        func.call(scope, this[i], i, this); 
    } 
}

最後に、次のような数値を操作する以外に、JavaScriptでこれらのメソッドを実際に使用する方法はありますか(データベースを更新していないため)。

alert([1,2,3,4].map(function(x){ return x + 1})); //this is the only example I ever see of map in javascript.

返信ありがとうございます。


mapおよびで行ったように、関数のネイティブJavaScriptの定義をどのように見つけることができますforEachか?Googleから入手できるのは、使用法の仕様とチュートリアルだけです。
WoodrowShigeru 2017年

言語に依存しないも参照してくださいforeachとmapの間に違いはありますか?
ベルギ

回答:


94

本質的な違いmapforEach、あなたの例では、あるforEachのに対し、元の配列要素に作用しmap、明示的に結果として、新しい配列を返します。

ではforEachあなたが何らかのアクションを取っている-と、必要に応じて変更-元の配列の各要素を。このforEachメソッドは、各要素に指定した関数を実行しますが、何も返しません(undefined)。一方、map配列をウォークスルーし、各要素に関数を適用して、結果を新しい配列として出力します。

「副作用」とforEachは、元の配列が変更されることです。「副作用なし」とmapは、慣用的に使用すると、元の配列要素が変更されないことを意味します。新しい配列は、元の配列の各要素の1対1のマッピングです。マッピング変換は、提供される関数です。

関係するデータベースがないという事実は、結局のところ、どの言語でもプログラミングの本質の1つであるデータ構造を操作する必要がないことを意味しません。最後の質問と同様に、配列には数値だけでなく、オブジェクト、文字列、関数などを含めることができます。


4
将来からの注意:この答えは実際にはナンセンスです。(要素の変更を含む).mapすべての.forEachことができるので、イテレータ関数から作成された新しいリストを返すだけです。
Travis Webb

6
過去からの返答:私は敬意を表しません。.map()新しい配列を作成し、元の配列を変更しません。実際に、それ自体がマップされている配列を変更することもできますが、それは、無意味ではないとしても、少なくとも非慣用的です。.forEach()は同様ですが、その機能を各要素に適用しますが、常にを返しますundefined。上記のすべてにもかかわらず、OPは、配列プロトタイプに追加された彼自身の特定の関数についても尋ねています。過去にここにES5はありませんでした。
Ken Redler、2015年

4
forEachできないことはまだ何もありませんmap。からの戻り値を気にmapする必要はありませんforEach。違いは、map結果に基づいて新しい配列を作成したくないタスクに使用するのは非常に効率が悪いことです。そのため、それらを使用しますforEach
突く

2
@poke:..そして同様に、プレーンな古いforループで必要なことは何でもできます。「有効性」に違いがあることについては異論はありませんが、一方が何らかの方法で他方がどうにかして達成できない魔法があると誰もが主張しているとは思いません。実際にmapは実行できないことが1つあります。それは、配列を返さないことです。JSのような機能お気に入りの振る舞いに関して、他の言語からの期待に応えたでありますの値mapreducefilterなどは、例えば、あなたが実装できるreduceRight類似のビルディング・ブロックを使用して、なぜ単に使用しませんかreduceRight
Ken Redler、2015

1
@ChinotoVokro、あなたの例は、返されたオブジェクト内に割り当てることにより、元の配列を明示的b.val = b.val**2 に変更しています。それがまさに私たちが言っている正確なことですが、実際には混乱しやすく、慣用的ではありません。通常は、単にも元の配列に割り当てていない、新しい値を返す:a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
ケンRedler

66

2つの方法の間の主な違いは、概念と文体です:あなたが使用してforEach、あなたが何かをしたいときまたは配列の各要素(「と」やってあなたは「副作用」の意味を挙げポストは、私が何を考えています)一方、配列の各要素を(元の要素を変更せずに)コピーおよび変換するmap場合に使用します。

mapforEachは両方とも配列内の各項目の関数を呼び出し、その関数はユーザー定義であるため、一方を使ってできることはほとんどなく、もう一方ではできません。醜いですが、map配列をインプレースで変更したり、配列要素で何かをしたりすることは可能です:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
    el.val++; // modify element in-place
    alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]

しかし、使用する意図については、より明確で明確forEachです:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) { 
    el.val++;
    alert(el.val);
});

特に、現実の世界でelはよくあることですが、人間が読める形式の有用な変数である場合は、次のようになります。

cats.forEach(function(cat) { 
    cat.meow(); // nicer than cats[x].meow()
});

同様に、簡単に使用forEachして新しい配列を作成できます。

var a = [1,2,3],
    b = [];
a.forEach(function(el) { 
    b.push(el+1); 
});
// b is now [2,3,4], a is unchanged

しかし、使用する方がきれいmapです:

var a = [1,2,3],
    b = a.map(function(el) { 
        return el+1; 
    });

またmap、新しい配列を作成するため、特に大規模な配列の場合、反復のみが必要な場合、少なくともパフォーマンス/メモリヒットが発生する可能性があることに注意してください。http://jsperf.com/map-foreachを参照してください。

これらの関数を使用する理由については、JavaScriptで配列操作を実行する必要があるときはいつでも役に立ちます(ブラウザー環境でJavaScriptについて話しているだけでも)、ほとんどの場合、コードで手作業で書き留めていない配列にアクセスしている。ページ上のDOM要素の配列、AJAXリクエストから取得したデータ、またはユーザーがフォームに入力したデータを処理している可能性があります。私が実行する一般的な例の1つは、外部APIからデータをプルすることです。このAPIを使用mapして、データを必要な形式に変換forEachし、新しい配列を反復処理してユーザーに表示することができます。


.map()インライン要素を変更するためにいつも使用している人を見かけます。を使用.mapすることの主な利点であるという印象を受けました.forEach
チョビー

1
@chovy一連の結果を作成するかどうかに基づいて選択する必要があると思います。.map要素を変更できますが、不要な配列が作成されます。また、どちらかを選択すると、読者に副作用があるかどうかを読者が推測させる方法も考慮する必要があります。
leewz

16

投票された回答(Ken Redlerから)は誤解を招くものです。

副作用コンピュータサイエンス手段における関数/メソッドのプロパティを変化させることグローバル状態[ウィキ]。狭い意味では、これには、引数からではなく、グローバルな状態からの読み取りも含まれる場合があります。命令型またはオブジェクト指向プログラミングでは、ほとんどの場合、副作用が発生します。そして、あなたはおそらくそれに気付かずにそれを利用しています。

間significiant差forEachmapすることであるmap割り当てメモリに格納戻り値は、一方では、forEachそれを離れて投げます。詳細については、emca specを参照してください。

forEach副作用が欲しいときに使われると言われる理由は、の返り値forEachが常にだということですundefined。副作用がない場合(グローバル状態が変更されない場合)、関数はCPU時間を浪費しているだけです。最適化コンパイラは、このコードブロックを削除し、最終的な値(undefined)に置き換えます。

ちなみに、JavaScriptには副作用に関する制限はありません。内の元の配列は引き続き変更できmapます。

var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]

1
この質問とその回答およびコメントは、数年おきに戻るのが楽しいものです。メモリ割り当ての観点から、JSでマップを実装する方法は、もう1つの素晴らしい角度です。
Ken Redler、

6

これは意外な答えのある美しい質問です。

以下は、の公式説明にArray.prototype.map()基づいています。

ありません、何もforEach()それを行うことができますmap()することはできませんが。つまり、map()はの厳密なスーパーセットですforEach()

map()、通常は新しい配列を作成するために使用され、可能にも現在の配列を変更するために使用されます。次の例はこれを示しています。

var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16]  As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!

上記の例でaは、はのように設定されてa[i] === ii < a.lengthます。それでも、それはのmap()能力、特にそれが呼び出される配列を変更する能力を示しています。

注1:
公式の説明は、それが呼び出される配列の長さmap()さえも変更する可能性があることを示唆しています!ただし、これを行う(正当な)理由がわかりません。

注2:
ながらmap()マップはのスーパーセットでありforEach()forEach() 1は、変更指定された配列を望むところまだ使用する必要があります。これにより、意図が明確になります。

これがお役に立てば幸いです。


8
実際、forEachができることの1つは、マップができないこと、つまり配列を返さないことです。
Antti Haapala 14

1
:あなたはまた、代わりに、スコープ変数の、ターゲット配列を変異させる写像関数の3番目の引数を使用することができますmapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
pdoherty926

@ pdoherty926はtrueですが、そうすることもできa.forEach(function(x, i, arr) { ...ます。これは、ある配列から別の配列に値をマップするのではなく、元の各項目を変更する意図がある場合、概念的にはより正しい方法です
conny

@conny:そうですね。また、同様の質問に対するこの回答をご覧ください。
Sumukh Barve 2016

3

mapそのままお使いいただけますforEach

ただし、必要以上のことを行います。

scope任意のオブジェクトにすることができます。必ずしもそうではありませんthis

mapandの実際の用途があるかどうかについてはforEachforまたはfor whileループの実際の用途があるかどうかを確認します。


しかし、なぜ「スコープ」と「this」の両方がここで呼び出されるのですか。func.call(scope、th​​is [i]、i、this); スコープは、現在のオブジェクト、つまり「これ」に等しいパラメーターではありませんか?
JohnMerlino、2014年

いいえ、現在のオブジェクトと同じにすることができます。オブジェクト自体は、3番目のパラメーターとして配列に渡されます。scope = scope || thisscope偽造(未定義、null、falseなど)の場合、スコープをthis代わりに設定して続行する」ことを意味します。
ウォンブルトン

これと等しくない場合の例に私をリンクできますか?
JohnMerlino

developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…には、「オブジェクトメソッドを使用した配列の内容の印刷」の下に1つあります
wombleton

1

これまでの質問はすべて正しいですが、私は間違いなく別の区別をします。使用mapとは、forEach意図を暗示することができます。

map既存のデータを何らかの方法で単純に変換しているときに使用したい(ただし、元のデータが変更されていないことを確認したい)。

forEachその場でコレクションを変更するときに使用するのが好きです。

例えば、

var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
    return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
//  [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
//  [{ val: 3 }, { val: 4 }, { val: 5 }]

親指の私のルールは確認時にあなたが作るされているmapあなたは、常にソースリストの各要素のために戻すために、いくつかの新しいオブジェクト/値を作成しているの復帰します何らかの操作を実行するのではなく、それこと。

既存のリストを実際に変更する必要がない限り、それを適切に変更しても意味がなく、関数型/不変のプログラミングスタイルによりよく適合します。


1

TL; DR回答-

mapは常に別の配列を返します。

forEachはしません。それが何をするかを決めるのはあなた次第です。必要な場合は配列を返し、必要ない場合は別のことを行います。

特定の状況では柔軟性が望ましい。それがあなたが扱っているものではない場合は、マップを使用してください。


0

もう一度、私は5年前に尋ねられた質問にネクロマンサーが回答を追加するように感じます(幸い、かなり最近の活動があるので、死者を悩ませているのは私だけではありません:)。

他のユーザーは、機能の違いに関する主な質問について既に投稿しています。しかし…

次のような数値を操作する以外に、JavaScriptでこれらのメソッドを実際に使用していますか(データベースを更新していないため)。

...おかしいのはおかしい ちょうど今日、変換のためにマップを使用して、正規表現からの複数の値を複数の変数に割り当てるコードを書きました。これは、非常に複雑なテキストベースの構造を視覚化可能なデータに変換するために使用されましたが、簡単にするために、日付文字列を使用した例を示します。 、マップの代わりに、Date-objectを使用しましたが、それはそれ自体で素晴らしい仕事をしていました。

const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';

var [
    iYear,
    iMonth,
    iDay,
    iHour,
    iMinute,
    iSecond,
    iMillisecond
    ] = DATE_REGEXP
        // We take our regular expression and...
        .exec(TEST_STRING)
        // ...execute it against our string (resulting in an array of matches)...
        .slice(1)
        // ...drop the 0th element from those (which is the "full string match")...
        .map(value => parseInt(value, 10));
        // ...and map the rest of the values to integers...

// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);

したがって、これは単なる例であり、データの非常に基本的な変換のみを行っただけです(例としてのみ)...マップなしでこれを行うと、はるかに退屈な作業になります。

確かに、それはJavaScriptのバージョンで書かれているので、あまり多くのブラウザー(少なくとも完全に)サポートしているとは思いませんが、それを実現しています。ブラウザで実行する必要がある場合は、適切にトランスパイルされると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.