这是我参与更文挑战的第7天,活动详情查看: 更文挑战。
从第一道面试小题看起:
console.log(a);
a();
var a = 3;
function a (){
console.log(10)
}
console.log(a);
a = 6;
a()
思考片刻,想必大家都有了答案,下面来揭晓答案:
这里可能会有几个疑问:
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 (即时)编译器进一步优化。
所有这些都发生在执行上下文中。因此,执行上下文是执行代码的特定部分的环境。执行上下文又俗称作用域,在ES6之前,只有全局作用域(全局可执行上下文)和函数作用域(函数可执行上下文)当然包括了with 和 catch所产生作用域。
执行上下文
在执行的上下文中是分为两个阶段:
- 解析阶段
- 执行阶段
在解析阶段中,以var a = 1为例子,是将var a和a = 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是不存在变量提升的,原因也很简单,因为const和let会形成块级作用域,而且在声明之前会出现暂时性死区,不允许访问声明的变量。
总结
1、var声明的变量和函数声明会提升,而const let不会。
2、var声明变量提升是高于同声明函数的。