[JS] foreach, for...of, and for...in

Array.forEach()

  • 只適用於 Array (類陣列也可以)
  • 將陣列內的每個元素,皆傳入並執行給定的函式
  • 不像 map() 或 reduce(), forEach 最後一個值一定會回傳 undefined
  • forEach 是不可鏈接的
1
2
3
arr.forEach(function callback(currentValue[, index[, array]]) {
//your iterator
}[, thisArg]);

參數

  • currentValue: 代表目前被處理中的 Array 之中的那個元素。
  • index (optional): 代表目前被處理中的 Array 之中的那個元素的index.
  • array (optional): 呼叫 forEach() 方法的那個 Array 本身,也就是上面語法中的 arr。
  • thisArg (optional): 執行 callback 回呼函式的 this(即參考之 Object)值。

thisArg 的用法範例

  • 如果是使用箭頭函式,可以直接忽略 thisArg,因為箭頭函式會直接綁定 this。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function Counter() {
    this.sum = 0;
    this.count = 0;
    }
    Counter.prototype.add = function(array) {
    array.forEach(function(entry) {
    this.sum += entry;
    ++this.count;
    }, this);
    };

    const obj = new Counter();
    obj.add([2, 5, 9]);
    obj.count; // 3
    obj.sum; // 16

for…in

  • for…in 迴圈只迭代 enumerable properties
  • for…in 迴圈迭代物件屬性的順序是隨意的。
  • for…in 不應該用來迭代一個索引順序很重要的陣列。 陣列索引只是以整數命名的可列舉屬性,其他方面等同於一般物件屬性。 無法擔保 for…in 以特定順序傳回索引,並且它將傳回全部可列舉屬性,包括非整數名的,以及繼承而來的可列舉屬性。如果順序是重要的,那應該要考慮使用 forEach 或 for…of。
1
2
3
for (variable in object) { 
...
}

參數

  • variable: A different property name is assigned to variable on each iteration.
    object
  • Object: whose enumerable properties are iterated.

用法

1
2
3
4
5
6
7
8
9
10
var obj = {a: 1, b: 2, c: 3};

for (const prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

for…of

  • for…of 會 loop through 可迭代的物件(包括: String, Array, Array-like arguments, or NodeList objects, TypedArray, Map and Set, and user-defined iterables)
1
2
3
for (variable of iterable) {
statement
}

參數

  • variable: On each iteration a value of a different property is assigned to variable.
  • iterable: Object whose iterable properties are iterated.

用法

1
2
3
4
5
6
7
8
9
10
let iterable = [10, 20, 30];

// 也可以使用 const 定義變數,如果不會再改變的話。下面使用 let 是因為 value 之後還要再做轉換。
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31

for…in VS for…of

以下節錄自 MDN,因為無法很好的翻成中文,英文的說明也比較簡潔易懂。

  • Both for…in and for…of statements iterate over something. The main difference between them is in what they iterate over.
  • The for…in statement iterates over the enumerable properties of an object, in an arbitrary order.
  • The for…of statement iterates over data that iterable object defines to be iterated over.

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // logs 0, 1, 2, "foo"
}
}

for (let i of iterable) {
console.log(i); // logs 3, 5, 7
}

這個例子可以很清楚地看出兩者的不同點。
一段一段拆分出來看。

1
2
3
4
5
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

上面這段 code,Object 和 Array 的原型分別加進了 objCustom. arrCustom。
因此,iterable 這個陣列也因為繼承和原型鏈的關係繼承了這兩個屬性。

1
2
3
for (let i in iterable) {
console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

接著,使用 for…in 對 iterable 做迭代。它並不會 log 出 3,5,7 或是 'hello',因為他們都不是 enumerable 的屬性。
但會 log 出陣列的索引質以及 objCustom 和 arrCustom。0, 1, 2, "foo", "arrCustom", "objCustom"。他們都是 enumerable 的屬性。

1
2
3
4
5
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // logs 0, 1, 2, "foo"
}
}

這個迴圈和上一段的迴圈是很相像的,不過加入了一個 hasOwnProperty 條件檢查迭代的 enumerable 屬性是不是物件自有的不是繼承而來的。如果是,就 log 出來。
屬性 0, 1, 2, "foo" 都是 iterable 自有的屬性所以會顯示。但 arrCustom, objCustom 是繼承來的,就不會被 log 出來。

1
2
3
for (let i of iterable) {
console.log(i); // logs 3, 5, 7
}

這個迴圈迭代並 log 出 iterable作為一個可迭代的物件迭代後回傳的 value。其中並不包含該物件的其他屬性。

什麼是 Enumerable properties ?

一直在說 enumerable properties,究竟它是什麼?
enumerable 的中文是 - 可列舉的。
可列舉的屬性?又是指什麼?

根據 MDN 的解釋,

Enumerable properties are those properties whose internal [[Enumerable]] flag is set to true, which is the default for properties created via simple assignment or via a property initializer.

Jabascript 物件的屬性它們自己內部還有屬性,其中一個內部的屬性就是 enumerable
而該 enumerable 屬性被設為 true 的屬性,就可稱是可迭代屬性。(有點饒口)
如果想知道的更精確,可以閱讀 MDN 的文件。

參考文章

forEach MDN
for…in MDN
for…of MDN
“foreach” vs “for of” vs “for in” in JavaScript