forEach()
メソッドは与えられた関数を、配列の各要素に対して一度ずつ実行します。
このデモのソースファイルは GitHub リポジトリに格納されています。デモプロジェクトに協力したい場合は、https://github.com/mdn/interactive-examples をクローンしてプルリクエストを送信してください。
構文
arr.forEach(callback(currentValue[, index[, array]]) { // execute something }[, thisArg]);
引数
callback
- 各要素に対して実行するコールバック関数で、1 つから 3 つの引数を受け付けます。
-
currentValue
- 現在処理されている配列の要素です。
index
Optional- 配列内の
currentValue
の添字です。 array
OptionalforEach()
が呼び出されている配列です。
thisArg
Optionalcallback
内でthis
として使用する値です。
返値
undefined
です。
解説
forEach()
は、与えられた関数 callback
を配列に含まれる各要素に対して一度ずつ、昇順で呼び出します。インデックスプロパティが削除されていたり、初期化されていなかったりした場合は呼び出されません。(疎らな配列については、下記の例を参照。)
callback
は次の 3 つの引数で呼び出されます。
- 要素の値
- 要素のインデックス
- 走査されている配列
forEach()
に thisArg
引数が与えられると、callback
の呼び出し時にそのオブジェクトが thisArg
として使用されます。callback
によって究極に管理される this
の値は、関数から見える this
を特定する一般規則に従います。
forEach()
によって処理される配列要素の範囲は、callback
が最初に呼び出される前に設定されます。forEach()
の呼び出しが開始された後に追加された配列要素に対しては、callback
は実行されません。既存の配列要素が変更または削除された場合、callback
に渡される値は forEach()
がそれらを参照した時点での値になります。削除された配列要素を参照することはありません。既に参照された配列要素がイテレーションの間 (e.g. shift()
を使用して) に削除された場合、後の要素は飛ばされます。(下記の例を参照してください。)
forEach()
は配列の各要素に対して callback
関数を一度ずつ実行します。map()
や reduce()
と異なり、返値は常に undefined
であり、チェーンできません。チェーンの最後に副作用を生じさせるのが典型的な使用法です。
forEach()
は呼び出された配列を変化させません。(ただし callback
が変化させる可能性があります)
例外を発生する以外の方法で、forEach()
ループを止めることはできません。ループ中に中断する必要がある場合、forEach()
メソッドは適切な方法ではありません。
早期終了を行うには下記のような手段が適しています。
- 単純な for ループ
- for...of / for...in ループ
Array.prototype.every()
Array.prototype.some()
Array.prototype.find()
Array.prototype.findIndex()
他の Array のメソッドである every()
, some()
, find()
, findIndex()
は、配列の要素を検査する際、truthy の値を返すことで以降の繰り返しが必要であるかどうかを決めます。
forEach は同期関数を期待する
forEach
はプロミスを待ちません。forEach
のコールバックとしてプロミス (または非同期関数) を使用する場合は、その意味合いを理解しておくようにしてください。
コード例
let ratings = [5, 4, 5];
let sum = 0;
let sumFunction = async function (a, b)
{
return a + b
}
ratings.forEach(async function(rating) {
sum = await sumFunction(sum, rating)
})
console.log(sum)
// 本来期待される出力: 14
// 実際の出力: 0
ポリフィル
forEach()
は ECMA-262 規格の第 5 版で追加されたもので、この規格のすべての実装には存在しない可能性があります。これを回避するには、スクリプトの最初に以下のコードを挿入して、ネイティブにサポートされていない実装でも forEach
を使用できるようにします。
このアルゴリズムは、Object
と TypeError
が元の値を持ち、fun.call
が Function.prototype.call()
の元の値に評価されると仮定すると、ECMA-262 規格の第 5 版で指定されているものと全く同じです。
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype['forEach']) {
Array.prototype.forEach = function(callback, thisArg) {
if (this == null) { throw new TypeError('Array.prototype.forEach called on null or undefined'); }
var T, k;
// 1. Let O be the result of calling toObject() passing the
// |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get() internal
// method of O with the argument "length".
// 3. Let len be toUint32(lenValue).
var len = O.length >>> 0;
// 4. If isCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); }
// 5. If thisArg was supplied, let T be thisArg; else let
// T be undefined.
if (arguments.length > 1) { T = thisArg; }
// 6. Let k be 0
k = 0;
// 7. Repeat, while k < len
while (k < len) {
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty
// internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Call the Call internal method of callback with T as
// the this value and argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// 8. return undefined
};
}
例
初期化されていない値については何もしない (疎らな配列)
const arraySparse = [1,3,,7]
let numCallbackRuns = 0
arraySparse.forEach((element) => {
console.log(element)
numCallbackRuns++
})
console.log("numCallbackRuns: ", numCallbackRuns)
// 1
// 3
// 7
// numCallbackRuns: 3
// 備考: 見ての通り、存在しない 3 から 7 までの値では、コールバック関数が呼び出されません。
for ループから forEach への変換
const items = ['item1', 'item2', 'item3']
const copyItems = []
// before
for (let i = 0; i < items.length; i++) {
copyItems.push(items[i])
}
// after
items.forEach(function(item){
copyItems.push(item)
})
配列の内容の出力
メモ: 配列の内容をコンソールに表示するために、配列の整形済みのバージョンを表示する console.table()
を使用することができます。
以下の例では同じことを forEach()
を使用して行う他の方法を説明しています。
次のコードは配列の要素ごとに、コンソールに 1 行ずつ要素の内容を出力します。
function logArrayElements(element, index, array) {
console.log('a[' + index + '] = ' + element)
}
// 添字が 2 のものは、配列内のその位置にアイテムが存在しない
// ため、飛ばされていることに注意してください。
[2, 5, , 9].forEach(logArrayElements)
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
thisArg の使用
以下の (不自然な) 例は、配列の中の各項目からオブジェクトのプロパティを更新します。
function Counter() {
this.sum = 0
this.count = 0
}
Counter.prototype.add = function(array) {
array.forEach((entry) => {
this.sum += entry
++this.count
}, this)
// ^---- Note
}
const obj = new Counter()
obj.add([2, 5, 9])
obj.count
// 3
obj.sum
// 16
thisArg
引数 (this
) が forEach()
に提供されているため、callback
の呼び出しのたびにこれが渡されます。コールバックはこれを this
の値として使用します。
オブジェクトをコピーする関数
次のコードは与えられたオブジェクトのコピーを生成します。
オブジェクトのコピーを生成するには他にもいくつか方法があります。次のものは一つの方法であり、Array.prototype.forEach()
が ECMAScript 5 の Object.*
メタプロパティ関数を使用することでどのように動作するかを説明するために示しているものです。
function 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) // obj2 looks like obj1 now
配列が繰り返しの間に変更され、他の要素が飛ばされる場合
次の例では one, two, four をログ出力します。
値 two
を持つ項目に達した時、配列全体の最初の項目はシフトして外れ、すべての残りの項目が 1 つ上の位置に繰り上がります。four
が配列の以前の位置に来るため、three
が飛ばされます。
forEach()
は繰り返しの前に配列のコピーを生成しません。
let words = ['one', 'two', 'three', 'four']
words.forEach((word) => {
console.log(word)
if (word === 'two') {
words.shift()
}
})
// one
// two
// four
配列の平板化
次の例は学習目的だけのものです。内蔵メソッドを使用して配列を平板化したい場合は、Array.prototype.flat()
を使用することができます (ES2019 の一部となる予定で、一部のブラウザーではすでに実装済み)。
function flatten(arr) {
const result = []
arr.forEach((i) => {
if (Array.isArray(i)) {
result.push(...flatten(i))
} else {
result.push(i)
}
})
return result
}
// Usage
const nested = [1, 2, 3, [4, 5, [6, 7], 8, 9]]
flatten(nested) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
仕様
ブラウザーの互換性
BCD tables only load in the browser