JS中4种数组遍历方法( for 、forEach() 、for/in、for/of)的区别

JS中4种数组遍历方法( for 、forEach() 、for/in、for/of)的区别

我们有多种方法来遍历 JavaScript 的数组或者对象,而它们之间的区别非常让人疑惑。Airbnb 编码风格禁止使用 for/in 与 for/of,你知道为什么吗?

这篇文章将详细介绍以下 4 种循环语法的区别:

for (let i = 0; i arr.forEach((v, i) => { /* … */ })for (let i in arr)for (const v of arr)

语法

使用for和for/in,我们可以访问数组的下标,而不是实际的数组元素值:

  1. for (let i = 0; i 

    使用for/of,则可以直接访问数组的元素值:

    for (const v of arr) {    console.log(v);}
  2. 登录后复制

  3. 使用forEach(),则可以同时访问数组的下标与元素值:

  4. arr.forEach((v, i) => console.log(v));
  5. 登录后复制

  6. 非数字属性

  7. JavaScript 的数组就是 Object,这就意味着我们可以给数组添加字符串属性:

  8. const arr = ["a", "b", "c"];typeof arr; // 'object'arr.test = "bad"; // 添加非数字属性arr.test; // 'abc'arr[1] === arr["1"]; // true, JavaScript数组只是特殊的Object
  9. 登录后复制

  10. 4 种循环语法,只有for/in不会忽略非数字属性:

  11. const arr = ["a", "b", "c"];arr.test = "bad";for (let i in arr) {    console.log(arr[i]); // 打印"a, b, c, bad"}
  12. 登录后复制

  13. 正因为如此,使用for/in遍历数组并不好。

  14. 其他 3 种循环语法,都会忽略非数字属性:

  15. const arr = ["a", "b", "c"];arr.test = "abc";// 打印 "a, b, c"for (let i = 0; i  console.log(i, el));// 打印 "a, b, c"for (const el of arr) {    console.log(el);}
  16. 登录后复制

  17. 要点: 避免使用for/in来遍历数组,除非你真的要想要遍历非数字属性。可以使用 ESLint guard-for-in规则来禁止使用for/in

  18. 数组的空元素

  19. JavaScript 数组可以有空元素。以下代码语法是正确的,且数组长度为 3

  20. const arr = ["a", , "c"];arr.length; // 3
  21. 登录后复制

  22. 让人更加不解的一点是,循环语句处理['a',, 'c']与['a', undefined, 'c']的方式并不相同。

  23. 对于['a',, 'c'],for/inforEach会跳过空元素,而forfor/of则不会跳过。

  24. // 打印"a, undefined, c"for (let i = 0; i  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);}
  25. 登录后复制

  26. 对于['a', undefined, 'c'],4 种循环语法一致,打印的都是"a, undefined, c"

  27. 还有一种添加空元素的方式:

  28. // 等价于`['a', 'b', 'c',, 'e']`const arr = ["a", "b", "c"];arr[5] = "e";
  29. 登录后复制

  30. 还有一点,JSON 也不支持空元素:

  31. 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
  32. 登录后复制

  33. 要点: for/inforEach会跳过空元素,数组中的空元素被称为"holes"。如果你想避免这个问题,可以考虑禁用forEach:

  34. parserOptions:    ecmaVersion: 2018rules:    no-restricted-syntax:        - error        - selector: CallExpression[callee.property.name="forEach"]          message: Do not use `forEach()`, use `for/of` instead
  35. 登录后复制

  36. 函数的 this

  37. forfor/infor/of会保留外部作用域的this

  38. 对于forEach 除非使用箭头函数,它的回调函数的 this 将会变化。

  39. 使用 Node v11.8.0 测试下面的代码,结果如下:

  40. "use strict";const arr = ["a"];arr.forEach(function() {    console.log(this); // 打印undefined});arr.forEach(() => {    console.log(this); // 打印{}});
  41. 登录后复制

  42. 要点: 使用 ESLint no-arrow-callback规则要求所有回调函数必须使用箭头函数。

  43. Async/Await Generators

  44. 还有一点,forEach()不能与 Async/Await Generators 很好的"合作"

  45. 不能在forEach回调函数中使用 await

  46. async function run() {  const arr = ['a', 'b', 'c'];  arr.forEach(el => {    // SyntaxError    await new Promise(resolve => setTimeout(resolve, 1000));    console.log(el);  });}
  47. 登录后复制

  48. 不能在forEach回调函数中使用 yield

  49. function run() {  const arr = ['a', 'b', 'c'];  arr.forEach(el => {    // SyntaxError    yield new Promise(resolve => setTimeout(resolve, 1000));    console.log(el);  });}
  50. 登录后复制

  51. 对于for/of来说,则没有这个问题:

  52. 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);    }}
  53. 登录后复制

  54. 当然,你如果将forEach()的回调函数定义为 async 函数就不会报错了,但是,如果你想让forEach按照顺序执行,则会比较头疼。

  55. 下面的代码会按照从大到小打印 0-9

  56. 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();
  57. 登录后复制

  58. 要点: 尽量不要在forEach中使用 aysnc/await 以及 generators

  59. 结论

  60. 简单地说,for/of是遍历数组最可靠的方式,它比for循环简洁,并且没有for/inforEach()那么多奇怪的特例。for/of的缺点是我们取索引值不方便,而且不能这样链式调用forEach(). forEach()。

  61. 使用for/of获取数组索引,可以这样写:

  62. for (const [i, v] of arr.entries()) {    console.log(i, v);}
  63. 登录后复制

  64. 参考

  65. For-each over an array in JavaScript?

  66. Why is using forin with array iteration a bad idea?

  67. Array iteration and holes in JavaScrip

  68. 本文采用意译,版权归原作者所有原文:http://thecodebarbarian.com/for-vs-for-each-vs-for-in-vs-for-of-in-javascript.html

  69. 相关免费学习推荐:js视频教程

  70.  
  71. 更多编程相关知识,请访问:编程入门!!

  72. 以上就是JS4种数组遍历方法( for forEach() for/infor/of)的区别的详细内容,更多请关注【创想鸟】其它相关文章!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

点点赞赏,手留余香

给TA打赏
共0人
还没有人赞赏,快来当第一个赞赏的人吧!
    编程技术

    详解JavaScript中的作用域和作用域链

    2025-3-7 23:19:05

    编程技术

    3个值得收藏的实用nodejs软件包

    2025-3-7 23:19:15

    0 条回复 A文章作者 M管理员
    欢迎您,新朋友,感谢参与互动!
      暂无讨论,说说你的看法吧
    个人中心
    购物车
    优惠劵
    今日签到
    私信列表
    搜索