Array.prototype.forEach vs Array.prototype.map


授業中、高次関数で最も多く使われ、最も重要な4つはmap、filter、reduce、forEachです.reduceは普段もよく使われているのでよく知っていますが、それ以外にmap、filter、foreachはまだ慣れていないので(概念がまだ完全に位置づけられていない)、整理して、深く理解したいと思います.

なぜ重複しない文の高次関数を使用してEachとmapを行うのか


同じ複文やifなどの条件文には慣れていますが、実際に慣れていないと読みやすさがかなり悪くなります.また,アルゴリズム問題を解く際に頭が痛いのも,for文が重なったり,大量の繰り返し文と条件文が混同されたりするためである.(私も少し混同していますが、他人が理解しにくいのは当然ではないでしょうか…ㅠ)最大の混乱をもたらす反復文や条件文は使わない方がいいでしょう.では、for文を書かないでいったい何を書くのでしょうか.可能ですが、代替方法は常に存在します.
foreachやmap、reduce、filterなどの高次関数が表示されます.高次関数とは,コールバック関数を渡すことでfor文を繰り返し呼び出して置き換えることができる.そうすると、可読性が高く、混乱が少なくなります.

Array.prototype.forEach


まずforeachを詳しく知るためにMDNに行って使い方を知りました.
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
  • callback:各要素で実行されるコールバック関数で、3つのパラメータを受信します.
  • currentValue:処理する現在の要素インデックス
  • array:foreach()を呼び出す配列
  • thisArg:callback実行時に使用する値
  • ここでは一度だけ必要で、それ以外は外来です.forEachの特徴は,コールバック関数が値を返しても,常に未定義を返すことである.次に、各配列要素に対して最後まで関数を実行します.すなわち,途中で複文を終了することも,最後まで移動することもできないため,break,continueのような文は使用できないので,複文を最後まで移動するのがforeachを用いる適切な例である.
    では、まず、forとforeachの違いを例に挙げて説明します.
    const num = [2, 4, 6, 7, 10];
    const result = [];
    const result2 = [];
    
    //for 문
    for (let i = 0; i < num.length; i++) {
      result.push(num[i] * 2);
    }
    
    //forEach 메서드
    num.forEach(element => result2.push(element * 2));
    明らかに同じ結果値であったが,2行から1行に減少した.もちろん、この例のコードは簡単で、大きな違いはないかもしれませんが、コードが複雑になるとforEachメソッドの読み取りがよくなります.
    forEach()は、配列内の各要素に対して所定のコールバックを昇順に実行します.削除または初期化されていないインデックス属性は実行されません.(たとえば、疎配列)
    ここで「削除済みまたは初期化されていないインデックス属性は実行されません」という言葉はあまり理解できないので、例を探したいです.
    //초기화하지 않은 인덱스 속성에 대해서는 실행하지 않고(undefined를 반환하지않고) 무시하고 넘어간다.
    
    const array = [1, 2, , 4, 5];
    let count = 0;
    array.map(element => count++); // count = 4
    この例ではcountは5ではなく4回増加し、2と4の間にスペースがあるにもかかわらず初期化されていない.つまり、疎アレイ(アレイ内部のデータが連続して位置していない)では、存在しない要素は巡回せずにスキップされる(したがって、最終的には密集アレイが作成されているようだ...)
    しかし、今は好奇心があります.countも4ですが、undefinedはいったいどこにあるのでしょうか.次の例です.
    const array = [1,2,3,4,5].forEach(console.log);
    console.log(array);
    では、上の結果値はいくらですか.
    正解はUndefinedです.上の長さは5なので、対応する配列を5回回転して出力しますが、結果値は定義されていません.つまり、forEachメソッドは常にundefinedを返し、arrayという名前のオブジェクトに希望する値を含めることはできません.

    3つのパラメータの使用方法について説明します.
    const array = [1, 3, 5, 7, 9].forEach((item, index, arr) => {
      arr[index] = item * index;
      console.log(arr[index]);
    });
    これにより、3つのパラメータが得られ、必要な値が導出されます.
    class Person {
      personList = [];
    
      add(arr) {
        arr.forEach(function (person) {
          this.personList.push((person += 3));
        }, this);
      }
    }
    const person = new Person();
    person.add([1, 3, 5]);
    console.log(person.personList);
    ではforeachメソッドの内部では、これはどのようにバインドされますか?まずforeachに渡される内部関数は関数宣言式(値のため)であるため、通常の関数として呼び出されるため、グローバルを指します.ただし、オブジェクトを作成しない一般的な関数では、オブジェクトを参照するためのPropertyまたはメソッドが最初に自己参照変数であるため、意味がありません.
    したがってstrictmodeの場合、thisはundefinedを指す(したがって、コールバック関数は高次関数とは異なり、問題が発生した場合、通常は矢印関数またはbindを使用する)

    Array.prototype.map


    mapはforeachとは異なり、未定義の配列ではなく、元の配列の長さと完全に一致する新しい配列を返します.すなわち,元の配列の長さが4であれば,返す配列の長さも4である.これはmapを使う理由がforeachとは違うからです.mapの目的が別の値にマッピングされて新しい配列を返す場合、forEach文は真のforの代替用途です(forEachも定義されていない値を返します.forEachがforにも存在しないように)
    arr.map(callback(currentValue[, index[, array]])[, thisArg])
    1.callback:新しい配列要素を生成する関数.
    2.currentValue:処理する現在の要素のインデックス
    3.array:map()呼び出しの配列
    4.thisArg:callbackを実行するときにこの値として使用する値
    foreachと同様にコールバック関数が必要であり、その他はオプションです.受信したパラメータ数は同じであり,受信したコールバック関数を繰り返し呼び出すのも同様である.ただし、mapでは、配列の真ん中が空の場合、forEachのように無視してスキップするのではなく、返される配列で同じ空の値を維持します.
    では、mapをどこでよく使うか見てみましょう.
    class Prefixer {
      constructor(prefix) {
        this.prefix = prefix;
      }
    
      add(arr) {
        return arr.map(function (item) {
          return this.prefix + item;
        }, this);
      }
    }
    const prefixer = new Prefixer('-webkit-');
    console.log(prefixer.add(['transition', 'user-select']));
    これもclassなのでstrict modeが適用されます.また、mapで呼び出された内部関数は通常の関数として呼び出されるため、外部から渡されない場合は未定義を指します.
    特定のproperty値を抽出する場合にmapも使用できます.急に特定の値を抽出するのはfilterではありませんか?このように、返す配列が元の配列と同じ長さであればmapを使用するのが適切である.
    const friends = [
      { name: 'Ji', age: '20' },
      { name: 'Lee', age: '23' },
    ];
    const getValues = (friends, key) => friends.map(friend => friend[key]);
    console.log(getValues(friends, 'name')); //[ 'Ji', 'Lee' ]
    上記の例ではmapを使用します.正確には、元の配列の長さと返される配列の長さが同じであるためです.
    すなわち、map(propertyソート、property値の反転)は、既存の長さと同時に使用される.

    mapとforeachの違いと共通点


    共通点

  • 両方とも高次関数であり、因数として伝達されるすべてのコールバック関数を繰り返し呼び出す.
  • 差異

  • 疎配列の場合、forEachは空配列を巡回せずにそのままスキップ(カウントが空配列の場合は巡回回数が減少)し、mapは空配列を保持して結果に含める.
  • mapのすべての引数を巡回して得られる戻り結果は,既存の配列の長さと同じであるが,forEachは常にundefinedを返し,大きな違いがある.
  • forEachは重複文の代わりに使用される関数であり、mapは要素値を他の値にマッピングする新しい配列を生成する関数である.
  • mapメソッドの長さは、返される配列の長さと一致しなければならない.すなわち、返される配列の数がこの数より少ない場合(条件によって返される値が変化する場合)、mapではなくフィルタまたはreduceを使用する必要があります.
  • forEachは非純関数、mapは純関数です.したがって、値を変更する場合はforeachを使用することが望ましいが、foreachはできるだけ使用しないでください.
  • すなわち,両者の目的は異なり,的確に用いなければならない.

    n/a.結論


    すべての関数は似たような動作をしますが、原因は異なります.しかしそれを無視してはいけません.動作が似ているからといって勝手に使ってはいけません.私たちの目的に合わせて適切に使います(+副手効果も確認します).そのため、関数の使用を多く練習する必要があるようです.私もこのような練習が足りないので、もっといろいろな例に暴露しようと努力します.

    参考資料

  • Array.prototype.forEach
  • Array.prototype.map
  • 現代JavaScript Deep Dive