我们有多种方法来遍历 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 函数的 thisfor ,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 规则要求所有回调函数必须使用箭头函数。 Async/Await 与 Generators还有一点,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)的区别的详细内容,更多请关注模板之家(www.mb5.com.cn)其它相关文章! |