> 웹 프론트엔드 > JS 튜토리얼 > JS의 네 가지 배열 탐색 방법( for , forEach() , for/in, for/of) 간의 차이점

JS의 네 가지 배열 탐색 방법( for , forEach() , for/in, for/of) 간의 차이점

青灯夜游
풀어 주다: 2020-10-28 17:46:22
앞으로
2522명이 탐색했습니다.

JS의 네 가지 배열 탐색 방법( for , forEach() , for/in, for/of) 간의 차이점

JavaScript에서는 배열이나 객체를 탐색하는 여러 가지 방법이 있으며, 그 차이점은 매우 혼란스럽습니다. 에어비앤비 코딩 스타일for/in 및 for/of는 금지되어 있습니다. 이유를 아시나요?

이 글에서는 다음 네 가지 루프 구문의 차이점을 자세히 소개합니다.

    for (let i = 0; i < arr.length; ++i)
  • < code>arr.forEach((v, i) => { /* ... */ })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
로그인 후 복사

让人更加不解的一点是,循环语句处理[&#39;a&#39;,, &#39;c&#39;][&#39;a&#39;, undefined, &#39;c&#39;]的方式并不相同。

对于[&#39;a&#39;,, &#39;c&#39;]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);
}
로그인 후 복사

对于[&#39;a&#39;, undefined, &#39;c&#39;],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 (let i in arr)

for (const v of arr)

구문

🎜 for 및 < 사용 code >for/in을 사용하면 실제 배열 요소 값 대신 배열의 첨자에 액세스할 수 있습니다. 🎜
for (const [i, v] of arr.entries()) {
    console.log(i, v);
}
로그인 후 복사
로그인 후 복사
🎜 for/of를 사용하면 요소 값에 직접 액세스할 수 있습니다. 배열: 🎜 rrreee🎜 forEach()를 사용하면 배열의 첨자와 요소 값에 동시에 액세스할 수 있습니다. 🎜rrreee

비숫자 속성

🎜JavaScript 배열은 객체입니다. 즉, 배열에 문자열 속성을 추가할 수 있습니다: 🎜rrreee🎜 4개의 루프 구문, for/in만 숫자가 아닌 속성을 무시하지 않습니다: 🎜rrreee🎜 이 때문에 < code>for/in 배열을 반복하는 것은 좋지 않습니다🎜. 🎜🎜다른 3개의 루프 구문은 숫자가 아닌 속성을 무시합니다. 🎜rrreee🎜점: 실제로 반복하려는 경우가 아니면 for/in을 사용하여 배열을 반복하지 마세요. -숫자 속성. ESLint의 guard-for-in🎜 규칙을 사용하여 for/in. 🎜

배열의 빈 요소

🎜JavaScript 배열은
빈 요소 🎜. 다음 코드 구문은 정확하며 배열 길이는 3입니다. 🎜rrreee🎜 사람들을 더욱 혼란스럽게 만드는 것은 루프 문이 ['a',, 'c']를 처리한다는 것입니다. [ 'a', 정의되지 않음, 'c']는 동일하지 않습니다. 🎜🎜For ['a',, 'c'], for/inforEach는 빈 요소를 건너뛰고 for for/of는 건너뛰지 않습니다. 🎜rrreee🎜['a', 정의되지 않음, 'c']의 경우 4개의 루프 구문이 동일하며 "a, undefine, c"가 인쇄됩니다. 🎜🎜빈 요소를 추가하는 또 다른 방법이 있습니다: 🎜rrreee🎜한 가지 더, JSON은 빈 요소를 지원하지 않습니다: 🎜rrreee🎜포인트: for/in 및 < 코드 >forEach는 배열의 빈 요소를 "구멍"🎜. 이 문제를 피하려면 forEach를 비활성화하는 것이 좋습니다.🎜rrreee

function-this

🎜for,< code >for/in 및 for/of는 외부 범위에 this를 유지합니다. 🎜🎜forEach의 경우 화살표 함수를 사용하지 않으면 콜백 함수의 this가 변경됩니다. 🎜🎜Node v11.8.0을 사용하여 아래 코드를 테스트하면 결과는 다음과 같습니다. 🎜rrreee🎜포인트: ESLint의
no-arrow-callback🎜 규칙에 따르면 모든 콜백 함수는 화살표 함수를 사용해야 합니다. 🎜

Async/Await 및 Generators

🎜한 가지 더, forEach()는 Async/Await 및 Generators와 잘 "협력"할 수 없습니다. 🎜🎜forEach 콜백 함수에서 wait를 사용할 수 없습니다: 🎜rrreee🎜 forEach 콜백 함수에서 Yield를 사용할 수 없습니다: 🎜rrreee🎜For for/of</code > 그런 문제는 없습니다:🎜rrreee🎜물론 <code>forEach()의 콜백 함수를 비동기 함수로 정의하면 오류가 발생하지 않습니다. forEach
순서대로 실행 🎜 , 두통을 비교합니다. 🎜🎜다음 코드는 큰 것부터 작은 것까지 0-9를 인쇄합니다. 🎜rrreee🎜포인트: forEach에서 aysnc/await 및 생성기를 사용하지 마세요. 🎜

결론

🎜간단히 말하면 for/of는 배열을 순회하는 가장 안정적인 방법입니다. for</code보다 더 간결합니다. code> 루프. 그리고 <code>for/inforEach()만큼 이상한 특수 사례도 많지 않습니다. for/of의 단점은 인덱스 값을 얻는 것이 불편하고 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의 네 가지 배열 탐색 방법( for , forEach() , for/in, for/of) 간의 차이점의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:fundebug.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿