さて、今日も配列のprototypeを勉強していきます。
今回は forEachです!さっそくMDN Web Docsを見ていきます。
forEach() メソッドは、与えられた関数を、配列の各要素に対して一度ずつ実行します。
// 公式から抜粋
const array1 = ['a', 'b', 'c'];
array1.forEach((element) => console.log(element));
結構シンプルですね、配列に対して1つづつ何かをしたい場合はforEach()のようです。
(ちょっとmap()と似ている印象です)
ひとまず読み進めます。
引数
引数は2つ受け取れます。
- callbackFn
- thisの値
[1, 2, 3].forEach(function(val, index, array) {
// ここで val, index, array を使用
}, thisArg); // thisArg はオプショナルです
解説
forEach() メソッドは反復処理メソッドです。指定された関数 callbackFn を配列に含まれる各要素に対して一度ずつ、昇順で呼び出します。 map() と異なり、 forEach() は常に undefined を返し、連鎖させることはできません。典型的な使用する用途は、チェーンの終わりで副次効果を実行することです。
...mapでいいんじゃない? とも思いましたが、
forEachの中で完結するものを行う時
に適しています。例えば下記でしょうか。
- mapのように新しい配列を返さなくて良い
- 配列の各要素に対するログ出力
- 外部の変数の更新, データ送信
- DOMの操作
さらに読み進めます。
callbackFn は値が割り当てられている配列インデックスに対してのみ呼び出されます。疎配列で空のスロットに対しては呼び出されません。
確かに、空の場合はスルーされています。↓↓
const array1 = ['a', 'b', 'c',,,,,,'e'];
let numCallbackRuns = 0;
array1.forEach((element) => {
console.log(element) // > "a" "b" "c" "e"
numCallbackRuns++
});
console.log(numCallbackRuns) // 4だけ
値が何かしら入っていれば、しっかり反復に含めてくれます。↓↓
const array1 = ['a', 'b', 'c',null ,undefined ,false, 'e'];
let numCallbackRuns = 0;
array1.forEach((element) => {
console.log(element) // > "a" "b" "c" null undefined false "e"
numCallbackRuns++
});
console.log(numCallbackRuns) // 7
thisArg の使用
// 公式から引用
class Counter {
constructor() {
this.sum = 0;
this.count = 0;
}
add(array) {
array.forEach(function countEntry(entry) {
this.sum += entry;
++this.count;
}, this); // 第二引数にthisを使用
}
}
const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.count); // 3
console.log(obj.sum); // 16
forEachの第一引数であるコールバック関数に functionを使っています。
この場合、thisを第二引数に定義しないと動きません。
コールバック関数内のthisはデフォルトでグローバルオブジェクト(ブラウザでは window、Node.jsでは global)を指すためです。
ちなみにこれを回避するにはアロー関数にすればOK。
class Counter {
constructor() {
this.sum = 0;
this.count = 0;
}
add(array) {
array.forEach((entry) => {
this.sum += entry;
++this.count;
}); // this削除できる
}
}
参考 : 【JavaScript】アロー関数式を学ぶついでにthisも復習する話
別のclassをthisする場合はこんな形です。
class AnotherClass {
constructor() {
this.value = 10;
}
}
const anotherInstance = new AnotherClass();
const numbers = [1, 2, 3];
numbers.forEach(function (entry) {
this.value += entry;
}, anotherInstance); // AnotherClassのインスタンスをthisとして渡す
console.log(anotherInstance.value); // 16
オブジェクトをコピーする関数
const copy = (obj) => {
const copy = Object.create(Object.getPrototypeOf(obj));
const propNames = Object.getOwnPropertyNames(obj);
propNames.forEach((name) => {
const desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
};
const obj1 = { a: 1, b: 2 };
const obj2 = copy(obj1);
上記はProperty Descriptors , Prototypesごとコピーします。
Property Descriptors ?
オブジェクトの各プロパティに関する詳細情報です。
これには、プロパティの値、プロパティが書き換え可能(writable)、列挙可能(enumerable)、設定可能(configurable)かどうかの情報が含まれます。
Prototypes?
オブジェクトが他のオブジェクトのプロパティを継承するための仕組みです。
forEachについて、復習することができました!
第二引数にthisを指定することはあまりなかく、忘れていたのでよかったです。
どんどん学習を続けましょう!!