首頁 > web前端 > js教程 > 主體

JS中4種陣列遍歷方法( for 、forEach() 、for/in、for/of)的區別

青灯夜游
發布: 2020-10-28 17:46:22
轉載
2496 人瀏覽過

JS中4種陣列遍歷方法( for 、forEach() 、for/in、for/of)的區別

我們有多種方法來遍歷 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)

#使用forfor/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; // &#39;object&#39;

arr.test = "bad"; // 添加非数字属性

arr.test; // &#39;abc&#39;
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/inforEach會跳過空元素,而forfor/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"。

還有一種加入空元素的方式:

// 等价于`[&#39;a&#39;, &#39;b&#39;, &#39;c&#39;,, &#39;e&#39;]`
const arr = ["a", "b", "c"];
arr[5] = "e";
登入後複製

還有一點,JSON 也不支援空元素:

JSON.parse(&#39;{"arr":["a","b","c"]}&#39;);
// { arr: [ &#39;a&#39;, &#39;b&#39;, &#39;c&#39; ] }

JSON.parse(&#39;{"arr":["a",null,"c"]}&#39;);
// { arr: [ &#39;a&#39;, null, &#39;c&#39; ] }

JSON.parse(&#39;{"arr":["a",,"c"]}&#39;);
// SyntaxError: Unexpected token , in JSON at position 12
登入後複製

重點: for/inforEach會跳過空元素,陣列中的空元素稱為"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
登入後複製

函數的this

forfor/infor/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規則要求所有回呼函數必須使用箭頭函數。

Async/Await 與 Generators

還有一點,forEach()不能與 Async/Await 及 Generators 很好的"合作"。

不能在forEach回呼函數中使用await:

async function run() {
  const arr = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
  arr.forEach(el => {
    // SyntaxError
    await new Promise(resolve => setTimeout(resolve, 1000));
    console.log(el);
  });
}
登入後複製

不能在forEach回呼函數中使用yield:

function run() {
  const arr = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
  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/inforEach()那麼多奇怪的特例。 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中文網其他相關文章!

相關標籤:
來源:fundebug.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板