JavaScript 迴圈:for 迴圈、forEach、for...in、for...of 一次搞清楚
在學習迴圈的過程中,有好幾個可以使用的方法,這次來一次搞清楚這些用法的差別,以及他們的使用時機,Let's go !!
for
一開始學迴圈最先會的語法,迴圈起始、結束都可以自己設定。
for(let i = 0; i < 5; i++){
console.log(i);
}
//output:0 1 2 3 4
在還沒有 let
之前,在 for 迴圈使用 var
宣告變數,會有洩漏成全域變數的問題,所以現在都使用 let
宣告迴圈起始值。
var i = 1;
for (var i = 0; i < 5; i++) {
dosomething...
}
console.log(i);
//output:5,被 for 迴圈改變了全域變數的值
for 迴圈還有一個特性是可以使用 break 或 return 跳脫迴圈
for (let i = 0; i < 5; i++) {
console.log(i);
if (i === 2) break; // i = 2 時迴圈結束
}
//output:0 1 2
forEach
學習 Array(陣列)後,會學到的語法,它可以對陣列進行迴圈,寫起來比 for 迴圈精簡,來比較看看:
const arr = [1, 2, 3];
// for loop
for(let i = 0; i < arr.length; i++){
console.log(arr[i]); // 1 2 3
}
// forEach loop
arr.forEach(element => console.log(element)); // 1 2 3
是不是精簡超多!!用過之後就回不去了~~
不過缺點就是不能用 break 或 return 結束迴圈。
for...in
跟 forEach 是同時在 ES5 推出的,可以遍歷 Array 跟 Object,並改善了 forEach 不能用 break 跟 return 的缺點。
不過 for...in 遍歷的是 key ,在用陣列上也沒有比較方便,甚麼意思呢?來看一下範例:
◆ Array
const arr = [1, 2, 3];
for(let el in arr){
console.log(el); // 0 1 2
}
可以看到列出來的是陣列的 index,咦?我以為只有物件才有 key,原來陣列也有 key 嗎?
沒錯!因為陣列也是一個物件阿,所以他當然也有 key 跟 value,只不過陣列的 key 就是他的 index,所以我們才能透過 arr[0]
來取得想要的元素。
既然陣列是個物件,當然也可以給他新的 key 跟 value:
const arr = [1, 2, 3];
arr["name"] = "jim";
console.log(arr); // [1, 2, 3, name: 'jim']
console.log(arr["name"]); // jim
for (let key in arr) {
console.log(key); //0 1 2 name
}
但是畢竟我們用 Array 遍歷,通常是為了取得 value,所以比起用 arr[el]
的方式取值,還不如用 forEach 更方便一點。
除此之外,for...in 還有一個致命的缺點,就是他會遍歷到繼承到屬性,也就是說用 Array.prototype
定義的屬性也會被遍歷到,看個範例:
const arr = [1, 2, 3];
Array.prototype.name = "jim"; //自訂原型屬性
console.log(arr); // [1, 2, 3] 陣列不變
for (let key in arr) {
console.log(key); // 1 2 3 name
}
可以看到陣列裡沒有 name 這個 key,不過在 for...in 卻被列印出來了,這樣就很容易找不到問題在哪。所以 for...in 大多用來遍歷物件的 key。
◆ Object
直接看範例:
const obj = { name: "Jim", age: 24 };
for (let key in obj) {
console.log(key); // name age
}
不過跟 Array 一樣他也有同樣的缺點,就是繼承的屬性也會被遍歷:
function People(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
//新增一個 prototype function
People.prototype.getFullName = () => {
return this.firstName + this.lastName;
};
const person = new People("Jim", "Hence");
for (let key in person) {
console.log(key + ":" + person[key]);
}
/* output
firstName:Jim
lastName:Hence
getFullName:() => {
return this.firstName + this.lastName;
}
*/
可以看到繼承的屬性也會被遍歷,解決方法就是用 hasOwnProperty
去篩選掉繼承的屬性:
for (let key in person) {
if (!person.hasOwnProperty(key)) return;
console.log(key + ":" + person[key]);
}
/* output
firstName:Jim
lastName:Hence
*/
for...of
雖然 Array 也可以用 hasOwnProperty
進行篩選防止遍歷到繼承屬性,但是還是不太直覺,所以在 ES6 推出 for...of ,完美的解決 Array 的所有問題,不僅可以終止迴圈,遍歷的也是 value ,更不會遍歷到自定義或繼承的屬性值,使用起來乾淨漂亮:
const arr = [1, 2, 3];
for(let el of arr){
console.log(el); // 1 2 3
}
值得一提的是,for...of 遍歷的不只是陣列,只要是 iterable 物件 都可以遍歷,不知道 iterable 物件,可以參考甚麼是 iterable?
總結
來總結一下各個優缺點:
for | 優點 |
缺點 | 使用時機 |
for | 可以中途停止 | 寫起來較複雜 | 非陣列情況下還是很好用,例如列印乘法表 |
forEach | 寫起來簡潔 |
無法中途停止 | 陣列可以使用 |
for...in | 寫起來簡潔,可中途停止迴圈 |
陣列會遍歷到 key,且會遍歷到繼承屬性 | 較適合用在遍歷物件 |
for...of | 寫起來簡潔,可中途停止迴圈,且遍歷 value,且所有 iterable 物件皆可用 |
不能遍歷物件 | 陣列遍歷較常用的語法 |