我們有多種方法來遍歷 JavaScript 的陣列或對象,而它們之間的差異非常讓人疑惑。 Airbnb 編碼風格禁止使用 for/in 與 for/of,你知道為什麼嗎?
這篇文章將詳細介紹以下4 個循環語法的差異:
for (let i = 0; i < arr.length; i)
arr.forEach((v, i) => { /* ... */ })
for (let i in arr )
for (const v of arr)
#使用for
和for/in
,我們可以存取陣列的下標,而不是實際的陣列元素值:
for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } for (let i in arr) { console.log(arr[i]); }
使用for/of
,則可以直接存取陣列的元素值:
for (const v of arr) { console.log(v); }
使用forEach()
,則可以同時存取陣列的下標與元素值:
arr.forEach((v, i) => console.log(v));
JavaScript 的陣列就是Object,這意味著我們可以為陣列添加字串屬性:
const arr = ["a", "b", "c"]; typeof arr; // 'object' arr.test = "bad"; // 添加非数字属性 arr.test; // 'abc' arr[1] === arr["1"]; // true, JavaScript数组只是特殊的Object
4 種循環語法,只有for/in
不會忽略非數字屬性:
const arr = ["a", "b", "c"]; arr.test = "bad"; for (let i in arr) { console.log(arr[i]); // 打印"a, b, c, bad" }
正因為如此,使用for/in
遍歷陣列並不好。
其他3 種循環語法,都會忽略非數字屬性:
const arr = ["a", "b", "c"]; arr.test = "abc"; // 打印 "a, b, c" for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } // 打印 "a, b, c" arr.forEach((el, i) => console.log(i, el)); // 打印 "a, b, c" for (const el of arr) { console.log(el); }
要點: 避免使用for/in
來遍歷數組,除非你真的想要遍歷非數字屬性。可以使用 ESLint 的guard-for-in規則來禁止使用for/in
。
JavaScript 陣列可以有空白元素。以下程式碼語法是正確的,且陣列長度為3:
const arr = ["a", , "c"]; arr.length; // 3
讓人更加不解的一點是,循環語句處理['a',, 'c']
與['a', undefined, 'c']
的方式並不相同。
對於['a',, 'c']
,for/in
與forEach
會跳過空元素,而for
與for/of
則不會跳過。
// 打印"a, undefined, c" for (let i = 0; i < arr.length; ++i) { console.log(arr[i]); } // 打印"a, c" arr.forEach(v => console.log(v)); // 打印"a, c" for (let i in arr) { console.log(arr[i]); } // 打印"a, undefined, c" for (const v of arr) { console.log(v); }
對於['a', undefined, 'c']
,4 種循環語法一致,印出來的都是"a, undefined, c"。
還有一種加入空元素的方式:
// 等价于`['a', 'b', 'c',, 'e']` const arr = ["a", "b", "c"]; arr[5] = "e";
還有一點,JSON 也不支援空元素:
JSON.parse('{"arr":["a","b","c"]}'); // { arr: [ 'a', 'b', 'c' ] } JSON.parse('{"arr":["a",null,"c"]}'); // { arr: [ 'a', null, 'c' ] } JSON.parse('{"arr":["a",,"c"]}'); // SyntaxError: Unexpected token , in JSON at position 12
重點: for/in
與forEach
會跳過空元素,陣列中的空元素稱為"holes"。如果你想避免這個問題,可以考慮停用forEach
:
parserOptions: ecmaVersion: 2018 rules: no-restricted-syntax: - error - selector: CallExpression[callee.property.name="forEach"] message: Do not use `forEach()`, use `for/of` instead
for
,for/in
與for/of
會保留外部作用域的this
#。
對於forEach
, 除非使用箭頭函數,它的回呼函數的 this 將會改變。
使用Node v11.8.0 測試下面的程式碼,結果如下:
"use strict"; const arr = ["a"]; arr.forEach(function() { console.log(this); // 打印undefined }); arr.forEach(() => { console.log(this); // 打印{} });
要點: 使用ESLint 的no-arrow-callback
規則要求所有回呼函數必須使用箭頭函數。
還有一點,forEach()
不能與 Async/Await 及 Generators 很好的"合作"。
不能在forEach
回呼函數中使用await:
async function run() { const arr = ['a', 'b', 'c']; arr.forEach(el => { // SyntaxError await new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); }); }
不能在forEach
回呼函數中使用yield:
function run() { const arr = ['a', 'b', 'c']; arr.forEach(el => { // SyntaxError yield new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); }); }
對於for/of
來說,則沒有這個問題:
async function asyncFn() { const arr = ["a", "b", "c"]; for (const el of arr) { await new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); } } function* generatorFn() { const arr = ["a", "b", "c"]; for (const el of arr) { yield new Promise(resolve => setTimeout(resolve, 1000)); console.log(el); } }
當然,你如果將forEach()
的回呼函數定義為async 函數就不會報錯了,但是,如果你想讓forEach
按照順序執行,則會比較頭痛。
下面的程式碼會依照從大到小印出0-9:
async function print(n) { // 打印0之前等待1秒,打印1之前等待0.9秒 await new Promise(resolve => setTimeout(() => resolve(), 1000 - n * 100)); console.log(n); } async function test() { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(print); } test();
#要點: 盡量不要在forEach
中使用aysnc/await以及generators。
簡單地說,for/of
是遍歷數組最可靠的方式,它比for
循環簡潔,並且沒有for/in
和forEach()
那麼多奇怪的特例。 for/of
的缺點是我們取索引值不方便,而且不能這樣鍊式呼叫forEach()
. forEach()
。
使用for/of
获取数组索引,可以这样写:
for (const [i, v] of arr.entries()) { console.log(i, v); }
本文采用意译,版权归原作者所有
原文:http://thecodebarbarian.com/for-vs-for-each-vs-for-in-vs-for-of-in-javascript.html
相关免费学习推荐:js视频教程
更多编程相关知识,请访问:编程入门!!
以上是JS中4種陣列遍歷方法( for 、forEach() 、for/in、for/of)的區別的詳細內容。更多資訊請關注PHP中文網其他相關文章!