动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第1张图片

写在前边

某一外包公司小李,刚刚入门前端之后,老板就让他写一段前端 JS 项目代码,不料,这时之前学过Java的小李遇到了一个问题,对于经常写 Java代码的小李来说,这属于一个灵异事件。项目中的一段代码如下:

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第2张图片

小李越想越感到纳闷,明明我在打印之前,没有声明任何的变量呀,为什么还能使用未声明的变量,从而打印出 a 的值呢,是不是我的编译器出现了问题?遇到问题的小李,越想越奇怪,就又写了一段测试代码。

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第3张图片

按理说应该输出undefined呀,为什么会输出的结果为 10。小李越想越奇怪,到底哪里出现问题了。这时老板过来了,看了看代码,笑了笑说,你还是去先学学基础吧。就这样,小李在网上找到了小鹿的这篇教程,学完之后,才恍然大悟。


思维导图

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第4张图片

1、什么是变量提升?

我们首先要弄明白什么是变量提升?顾名思义,从表面上的意思去分析,代码在真正的执行之前变量就已经进行了提升声明。是的,确实是这么个意思,但是我们后边的原理部分说的这种所谓的“提升”却不是真正的提升,而是为了让开发者便于理解,才有了变量提升这以名词,话说到这,那么它是如何进行提升的呢?

我们还是用小李的例子,第一个例子如下:

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第5张图片
首先,程序执行,首先将声明的变量提升到最前边,a 变量就会被提升到最前边,但是并不会进行赋值操作,变量提升完毕之后,代码自上而下顺序执行。输出 a 的值,a 此时没有被赋值,所以输出 undefined,继续执行,a 被赋值 10,执行完毕。

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第6张图片
除此之外,其实不仅变量会提升函数也会被提升。

注意:只有 var 声明的变量或函数才会进行提升,而 ES6 中的 let 和 const 不会进行提升,后面会讲到。

函数提升:

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第7张图片

函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部。


3、为什么需要变量提升?

小李之前有 Java基础,为何到了 JS 有这种策略,他想不通为什么要这样,顺序执行它不香吗?

别着急,我们举个例子就说明为什么 JS 中需要变量提升了。如下代码所示:
动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第8张图片
我们想一想,如果 JS 没有变量提升,这个程序还是否能够执行呢?我们在 fn 函数中执行 fn2 函数,如果没有变量提升,此时的 fn2 还没有声明,所以找不到,抛出错误。

正是由于 JS 存在变量提升,所以程序执行,fn 和 fn2 函数提升被声明,然后再去执行下面的代码,就可以正常通过编译执行,是不是很香呢?最后得出结论为,变量提升存在的根本原因就是为了解决函数间互相调用的情况。


3、变量提升的内部原理

要想知道变量提升的内部原理,我们就深入 JS引擎的工作原理。javascript代码的执行事实上是分两个阶段进行的。

一旦 JS 创建了词法环境——程序执行的环境,就会执行第一阶段。在第一阶段,没有执行代码,但是javascript引擎会访问并注册在当前词法环境中所声明的变量和函数 —— 我们上边所说的变量和函数声明的提升。javascript在第一阶段完成之后开始执行第二阶段,代码自上而下顺序执行。

具体第一阶段是如何执行的呢?

1、如果我们声明的是一个函数环境,那么我们在词法环境中创建形参和函数参数的默认值,如果是非函数(全局作用域下),那么就跳过这个步骤。

2、如果是创建全局或函数环境,就扫描当前代码进行函数声明,不会扫描其他函数的函数体。但是不会扫描函数表达式或箭头函数。如果是块级作用域的话(ES6 的知识),跳过此步骤。

3、扫描当前代码进行变量声明。在函数或全局环境中,找到所有当前函数以及其他函数之外通过var声明的变量,并找到所有在其他函数或代码块之外通过let或const定义的变量。

在块级环境中,仅查找当前块中通过 let或 const定义的变量。对于所查找到的变量,如果没有赋值,则标记为 undefined;否则,对变量进行赋值。

整个处理过程如下图:
动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第9张图片
我们很多人认为,变量的声明提升至函数顶部,函数的声明提升至全局代码顶部。但是,我们知道原理之后,并没有那么简单。

声明:变量和函数的声明并没有实际发生移动。只是在代码执行之前,先在词法环境中进行注册。虽然描述为提升了,并且进行了定义,这样更容易理解 JavaScript 的作用域的工作原理,但是,我们可以通过词法环境对整个处理过程进行更深入地理解,了解真正的原理。


如何解决变量提升问题?

如果我们不想让变量或函数进行变量提升,我们有没有好的解决办法?

有的,在 ES6 新标准中,提供了 let和 const来声明变量,如果我们用 let和 const在声明之前使用了 a,程序就会抛出错误。我们把这个错误叫做暂时性死区,就是说我们不能在变量声明之前使用变量。

在这里插入图片描述
那么问题来了,var和 let/const声明的变量除了变量提升外,有什么本质上的区别呢?

第一,var声明的全局变量会绑定在window对象上,而 let和 const 声明的变量不会挂在到全局变量 window上。
动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第10张图片

动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第11张图片
第二,let和 const 区别就是,后者不能进行二次赋值。


5、大厂面试题解析

既然我们学了这么多有关变量提升的知识,光说不练假把式,直接上大厂面试题,检测一下你是否真正的掌握了变量提升。
动画:面试官问我 JS「变量提升」我头皮发麻,最后把这篇动画甩给了他_第12张图片
我们更具上边小鹿总结的规律,我们把这个大厂的题给梳理一遍。

程序执行,创建一个执行环境(词法环境),然后下一步将可以进行提升的变量进行提升,我们上边说过有哪几类是不能提升的?箭头函数、表达式。而且函数的提升会优先于变量的提升。说到这里,你一定知道这个题答案了,很明显的,答案为 8 。如果你没有做对,再将从头看一下文章哦!

6、小结

小李看到这里,马上就明白了项目出现的奇怪现象,后来小李被老板升职加薪了…

写到这里,对于每个基础的知识点要打牢,尤其是对于初学者,前期学习来很浮躁,比如:变量提升,知道有这么一回事,但是他并不知其所以然。有的小伙伴会问,学这么详细图个什么?

这个问题问的好,之所以小鹿将技术点分享的很详细,拆解的很散,但是有句话说的好,“授人以鱼,不如授人以渔”。我希望更能把这种学习的态度和方法分享给大家,一篇文章不止学到了一个知识点,而是能够提取出,属于自己的东西。这对你后期阅读源码等有很大的提升和帮助!


❤️ 不要忘记留下你学习的脚印 [点赞 + 收藏 + 评论]

文章+动画写了好几个小时,不妨点赞支持一下。嘻嘻,你不点赞说明你很自私,你怕那么好的文章让别人也看到。开个小小玩笑。

作者Info:

【作者】:小鹿

【原创公众号】:小鹿动画学编程。

【简介】:和小鹿同学一起用动画的方式从零基础学编程,将 Web前端领域、数据结构与算法、网络原理等通俗易懂的呈献给小伙伴。先定个小目标,原创 1000 篇的动画技术文章,和各位小伙伴共同努力一起学习!公众号回复 “资料” 送一从零自学资料大礼包!

【转载说明】:转载请说明出处,谢谢合作!~

你可能感兴趣的