比较for/foreach/for in/for of


title: 比较for/foreach/for in/for of
date: 2019-03-12 08:57:46
tags: js


alesia-kazantceva-283285-unsplash (1).jpg

本文翻译自 作者:code_barbarian For vs forEach() vs for/in vs for/of in JavaScript

在JS中有许多方式去循环数组和对象。但在实际使用中容易产生使用上的混淆,这需要我们去权衡。在某些方式上甚至禁止某些循环结构。(详情请看),本文我将重点描述四个循环结构迭代的方式:

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

我将使用几种不同的边缘情况概述这些循环结构之间的区别。我还将链接到相关的ESLint规则,您可以使用这些规则来强制在项目中执行从而达到循环的最佳实践。

语法概述

在for与for/in循环结构给你访问索引数组中,而不是实际的值。例如,假设您要打印出以下数组中存储的值:

const arr = ['a', 'b', 'c'];

使用for 和 for/in ,如果需要打印出值,则需要arr[i],例如:

for (let i = 0; i < arr.length; ++i) {
  console.log(arr[i]);
}

for (let i in arr) {
  console.log(arr[i]);
}

与其他两个结构相比,forEach()并且for/of,您可以访问数组元素本身。有了forEach()你可以访问数组索引i,for/of不能。

arr.forEach((v, i) => console.log(v));

for (const v of arr) {
  console.log(v);
}

非数值属性

js中数组是一种特殊的对象,这就意味着你可以添加string类型的属性值在你的数组里,不仅仅是数字。

const arr = ['a', 'b', 'c'];

typeof arr; // 'object'

// Assign to a non-numeric property
arr.test = 'bad';

4种循环结构有3种忽视非值属性,但是,for/in 却会打印出“bad”

function ab(){
    const arr = ['a','b','c'];
    arr.test = 'bad';

    
    for(let i in arr){
        console.log(arr[i]);
    }

    // for(let j of arr){
    //  console.log(j);
    // }

    // for(let i=0;i<arr.length;i++){
    //  console.log(arr[i]);
    // }

    // arr.forEach((v,i)=>{
    //  console.log(v);
    // })

}
ab();

打印的结果只有for/in 会展示“bad”的值

显而易见,这也是为什么在循环的时候,通常我们不会去选择for/in的原因,因为其他的循环方式都可以正确的忽视非数值的值 。

摘要:除非你要确定要迭代非数字键和继承键,否则请避免使用for/in

空元素
js数组允许空元素,在语法中这是合法的,并且下面的例子长度是3.

const arr = ['a',,'c'];

arr.length; //3

令人困惑的是,循环结构的处理['a','c'] 也有不同的['a',undefined,'c']。下面是4个循环结构如何处理的呢。for/in 、forEach会跳过空元素,但是for,for/of并不会。

function ab(){
    const arr = ['a',,'c'];

    for(let i in arr){
        console.log(arr[i]);
    }

    arr.forEach((v,i)=>{
        console.log(v);
    })

    for(let i of arr){
        console.log(i);
    }

    for(let i=0;i<arr.length;i++){
        console.log(arr[i]);
    }
}
ab();

此外还有另一种方式将空元素添加到数组中:

const arr = ['a', 'b', 'c'];
arr[5] = 'e';  //`['a', 'b', 'c',, 'e']`

forEach()并且for/in跳过在阵列中的空元素,for并且for/of没有。这种forEach()行为可能会导致问题,但JavaScript数组中的漏洞通常很少见,因为它们在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

函数上下文

函数上下文是js中很其他的一种方式this。for for/in for/of都可以保持自身之外的this的值,但是forEach()却有些不同,除非你使用剪头函数。

'use strict'; //严格模式下

const arr = ['a'];

// Prints "undefined"
arr.forEach(function() {
  console.log(this);
});

Async/Await and Generators

另外一个边缘情况对于forEach()就是它不会很好的工作,当与async/await一起使用的时候,如果说你的forEach()的回调是异步的这没关系,但是你不能在await里使用forEach();

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

同时你也不可以使用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,你也在异步中遇到很大的麻烦,下面这个列子将相反的顺序打印0-9

async function print(n) {
  // Wait 1 second before printing 0, 0.9 seconds before printing 1, etc.
  await new Promise(resolve => setTimeout(() => resolve(), 1000 - n * 100));
  // Will usually print 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 but order is not strictly
  // guaranteed.
  console.log(n);
}

async function test() {
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(print);
}

test();
如果你正在使用async/await或者generator,请记住这forEach()是语法糖,你应该谨慎使用使用。

结论

通常,for/of是JavaScript中迭代数组的最强大的方法。它比传统的更简洁的for循环,并没有许多边缘事例 for/in和forEach()。主要的缺点fro/of是你需要做额外的工作来访问索引(1),forEach()有几个边缘案例,应该谨慎使用,但是很多情况下,它使代码更简洁。

tips: 要在for/of循环中访问当前数组索引,可以使用Array#entries()函数。

for (const [i, v] of arr.entries()) {
  console.log(i, v); // Prints "0 a", "1 b", "2 c"
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • 由于贵人王琼的极力及三番两次推荐,王阳明的再次使用心学威力的机会来了——南赣剿匪。 1517年,王阳明到江西赣州剿...
    娟姐的心语话廊阅读 1,304评论 1 1
  • 爱,究竟是什么?它能让你哭、让你笑、让你痴、让你欲罢不能。多少年轻男女,一旦遭遇爱情,便犹如雾里看花、水中望月,只...
    好想和你说TellingYou阅读 1,019评论 0 0
  • 好久没有给你写信了,这期间发生了蛮多的事。你终于把婚礼办了,我的生活里也发生了些不大不小不明晰的变化。 ...
    蜗瓦阅读 92评论 0 0
  • 栖居于此时 真觉得山穷水尽了 小民生涯 平素唯知米价分厘间涨跌 一把二手自行车,漫寨游转 偷菜摸香蕉之在下啊 逃逛...
    昆南阅读 336评论 0 4