JS 之 for 循环的那些事儿

JS 之 for 循环的那些事儿

新的一年开始,是投简历,找工作,跳槽,升职加薪的黄金时期,当然也要有高操的技能和扎实的基本功才能行。

今天我们就来聊一聊 JS 中for循环的那些事儿。

首先来看一个非常常见的面试题:

下列代码会打印出什么结果?


    for (var i = 0; i < 10; i++) {
        setTimeout(() => {
            console.log(i);
        }, 1000);
    }

对于一个“行走江湖”多年的JS老鸟而言,这个问题真的是太基础、太入门、太没什么挑战性可言了,哪怕就是一个新手,答案也是张口就来:10个10

没错,结果百分百正确。但是如果面试官问你为什么呢?可能JS基本功扎实的“老鸟”能给出相当让人们以的答复,但是对于一些“菜鸟”而言,可就没那么简单了。

下面就让我们一行行的分析一下这段小小的代码,彻彻底底的搞明白这个for循环到底是怎么执行的,怎么就一蹴而就的打印了:10个10的。

    for(var i = 0;i<10;i++){
        
    }

这段代码是创建循环体,var 声明一个临时变量 i ,赋值 = 0;i的值 小于10,每次执行i++;

    setTimeout(() => {
        console.log(i);
    }, 1000);

这段代码是循环体内部的逻辑,调用setTimeout函数,没隔1000毫秒执行一次它的第一个参数(一个箭头函数或者说是匿名函数),在这个箭头函数内部执行打印语句。


好的,代码我们分析完毕了,下面我们来分析一下代码的执行逻辑。

for循环是一个同步执行的过程,也就是说,for循环体内部的业务逻辑会没有阻塞的一步步的执行下去;花括号{}包括的循环体是一个for循环作用域,var声明的变量i的作用域就是for循环体内部。

换言之,i在执行第一次循环的时候被创建,在堆内存中占有一个位置用来存放它的引用,赋初值 = 0,i的值被存放于栈内存中的某个地方;以后的每次循环执行,i这个变量的引用不会发生改变,改变(++操作)的只是栈内存中他的值而已(加了10次变成了10)

好的,我们分析完了i的变化,下面我们再来看循环体内部的变化。

首先setTimeout函数是一个异步函数,它是在多少时间间隔之后执行某个操作的意思。每次(执行10次)执行setTimeout函数,都会往时间循环队列(Event loop queue)里push一个匿名(箭头)函数,函数的逻辑很简单,就是一行打印操作,打印的内容是i,这个i就是for循环体定义的那个i,同一个引用,指向同一块栈内存地址,对应着同样的一个值。

当同步的for循环执行完毕之后,调用堆栈(call stack)开始对事件循环队列执行出队操作,本着队列先进先出的原则,里面的匿名函数被一个个的出队执行,进行打印i,同一个引用,相同的值(每次++之后),所以10个匿名函数打印的结果都是10

可能用了这么多的语言描述让人实在感到乏味,下面我们来看一张简单的图:

for.png

看完分析是不是觉得,这么一个小小的for循环其实内容还是挺丰富的,涉及到了var的声明变量,引用和值,作用域,堆/栈内存,事件循环队列,堆栈调用,setTimeout,同步/异步等。

想想看,如果被问及这个问题,如果你能细致入微的把这些都回答上来,是不是会让面试官耳目一新、眼前一亮,觉得你是一个不可多得的对JS有着很深刻研究的人才呢,不仅仅会给你的面试加分,甚至还有可能给你加薪呢!

当然这些都还只是我们一厢情愿而已了。说不定还有更变态的问题在后面,比如,面试官又问:

如果我想打印出0,1,2,3,……怎么做呢?

你可能想,这个问题我早就考虑到了,当下手写除了你认为完美的答案:

    for (let i = 0; i < 10; i++) {
        setTimeout(() => {
            console.log(i);
        }, 1000);
    }

对于写惯了ES6的你来说,这样简单易行的改写还不是手到擒来嘛。面试官看了看,面无表情的点了点头说,嗯,可以是可以,但是为什么呢?

你心想,这就有点不按套路出牌了,给了你正确答案就行了,怎么一个劲儿的不停的问为什么呢?

或许你也会在面试之前扪心自问过:为什么呢?为什么只是单纯的把var改成了let,就可以顺序打印出0,1,2,3……了呢?

然后你不由自主的上网搜索,终于在权威的MDN上找到了答案。

let声明的变量只在其声明的块或子块中可用,这一点,与var相似。二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数。 —— MDN

什么意思呢?

由于 let 声明的变量只在其块或者子块中起作用,也就可以理解为,这种for循环里会执行10次的let i;并且每一个 i 都有各自的内存空间地址,每个 i 都有其不同的值,且被绑定到了10个分别不同的匿名函数中,因此最终当事件循环队列里的匿名函数出队列执行时,打印的结果自然也就是其绑定的不同的 i 的值:0,1,2,3……了。

如果你还能把这个问题圆满的回答上来,估计面试官又会对你刮目相看三分了,当然还有更变态的会继续问你:

嗯,你回答的不错,那还有其他方式改写这段代码吗?

都到这份上了,你就干脆一下又把闭包的改写方法写了出来,并且又是细致入微的将闭包的原理给面试官讲了一遍,面试官估计在这个问题上是彻底的被你征服了。

好吧,碍于时间和主题划分问题,我们今天不讨论闭包的实现逻辑,只是把代码贴在这里了,有兴趣的小伙伴可以自行Google研究。等哪天有时间整理闭包的问题时,我们再把这个问题拿出来“鞭尸”!

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