从面试题中掌握Javascript的变量提升

170 阅读3分钟

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

从第一道面试小题看起:

console.log(a);
a();
var a = 3;
function a (){
  console.log(10)
}
console.log(a);
a = 6;
a()

思考片刻,想必大家都有了答案,下面来揭晓答案: image.png 这里可能会有几个疑问:

1、为什么第一行输出结果了,而不是抛出错误:Uncaught ReferenceError: a is not defined

2、为什么第二行函数调用成功;

3、为什么第二个console.log输出的不是函数;

带着上面的疑问,先了解什么是Javascript执行机制。

JavaScript执行机制

JavaScript是轻量级解释型语言。浏览器接受到JavaScript代码,并以代码自身的文本格式运行它。技术上,几乎所有JavaScript转换器都运用了一种叫做即时编译(just-in-time compiling)的技术;当JavaScript源代码被执行时,它会被编译成二进制的格式,使代码运行速度更快。尽管如此,JavaScript仍然是一门解释型语言,因为编译过程发生在代码运行中,而非之前。所以,JavaScript在执行过程中,需要将所写的代码转换成机器所识别的代码。过程如下:

1、Tokenizing:这个阶段,源代码字符串分解成多个有意义的块,这些块称为Tokens。

2、Parsing:标记数组转换为语言语法所理解的嵌套元素树。这棵树被称为AST(抽象语法树)。

3、Code Generation:解析阶段创建的 AST 将转换为可执行的字节码。然后,这个可执行字节码由 JIT (即时)编译器进一步优化。

execution_steps.gif

图片来源: freecodecamp

所有这些都发生在执行上下文中。因此,执行上下文是执行代码的特定部分的环境。执行上下文又俗称作用域,在ES6之前,只有全局作用域(全局可执行上下文)和函数作用域(函数可执行上下文)当然包括了withcatch所产生作用域。

执行上下文

在执行的上下文中是分为两个阶段:

  • 解析阶段
  • 执行阶段

在解析阶段中,以var a = 1为例子,是将var aa = 1分开,而且var a的声明是会提升到当前上下文最前面,优先级比同名函数高,且只会声明一次并初始化为undefined。这时候在当前上下文的作用域中变量对象(VO)出现了a,值为undefined

在执行阶段,执行到var a = 1实际上是赋值操作,执行的是a = 1。这时候在当前上下文的作用域中变量对象(VO)出现了a,值为1

ES5 var VS ES6 const let

在es5中的var存在变量提升,但是ES6中的const let是不存在变量提升的,原因也很简单,因为constlet会形成块级作用域,而且在声明之前会出现暂时性死区,不允许访问声明的变量。

总结

1、var声明的变量和函数声明会提升,而const let不会。

2、var声明变量提升是高于同声明函数的。