变量提升——透过现象看本质
变量提升可谓是JS中最基础最常见的知识点了,大家都知道当用var声明一个变量a时,a似乎并不是在声明语句的那一行才被创建的。因为早在当前作用域的顶端,就已经有了一个值为undefined的a。 就好像a是被提前到了作用域的顶端先行创建,并赋值为undefined。而到了a真正声明并赋值的那一行时,再为a进行赋值。
于是这个现象就被大家称为——变量提升。
但在代码解析执行的过程中,是真的有那么一个“人”将变量拿到了作用域的顶端先创建,而后再进行赋值的吗?
要知道这个问题的答案,我们就需要透过现象,去探寻变量声明和赋值的本质。
var a = 1; 复制代码
这是一条最简单的变量声明赋值语句,在我们看来这是一条连贯的声明赋值语句,在声明的同时就为变量进行了赋值。然而在引擎和编译器看来,这里其实有两个不同的声明,而且这两个不同声明还是由浏览器两个不同的模块进行处理的。
首先,编译器会将这条语句分解成词法单元,然后将分解得到的词法单元解析成一个树结构,接着编译器在遇到
var a
时,会在当前作用域中查找是否有同名变量,如果有,编译器会忽略该声明,继续向下编译;如果没有,则会在当前作用域集合中声明一个新的变量,并命名为a
。编译器声明完变量后,会生成一些引擎运行时所需的代码,这些代码被用来处理
a = 2
这个赋值操作。由引擎运行这些代码,查询作用域是否存在变量a
,如果存在,引擎直接使用变量a
为其赋值1
;如果不存在,引擎则会沿着作用域链继续向上查找,直到找到全局作用域。若全局作用域还是没有,引擎就会抛出一个异常。
通过上述的流程我们可以清晰地得知,一条变量声明赋值语句其实是由编译器和引擎两个模块互相配合解析处理的,编译器解析声明变量在前,引擎查找赋值变量在后,于是便导致了我们所看到的“变量提升”的现象。
总结
综上所述:在整个解析执行的过程中,声明赋值语句的位置并没有发生过变化,也就不存在“提升”的概念。变量声明和赋值的错位只是由于js编译器和引擎分工有先后带给我们的错觉罢了。不过虽然变量提升的本质并非是字面意思,但这个说法却将现象描绘地十分生动形象,于是也就这么流传下来了。 最后,希望这篇文章能够带给你一个不一样的视角,让你对js的变量声明和赋值有更加深入的了解。
作者:拂衣去
链接:https://juejin.cn/post/7019517355038343199