JS块级作用域的隐藏细节!
前言
最近有群友问我下面这段代码为什么会这样输出
{
function foo (){}
foo= 1;
console. log (foo); //1
}
console. log(foo) //function
对于上述的输出结果你会感到意外么?
这个问题我一看,顿时就笑出声了,因为至少有十个人问过我这个问题。所以我干脆写一篇文章来详细的回答下这个问题。
实际上,会有这种 "奇特" 的输出结果,与 JS 的块级作用域发展历史有关。
JS 块作用域
众所周知,在 ES6 之前,JS 中还是 var
满天飞的模式,没有传统编程语言块级作用于域这个重要的特性。终于,在 ES6 中加入了 let
const
关键字来弥补这个缺失。至于 let
和 const
是如何实现块级作用域的,你可以看我这篇文章:
掘金: 块级作用域的本质
你也可以看我的 JS进阶教学视频:
有了块级作用域后,JS的开发明显和谐了很多。
请看下面的代码:
if (true) {
let a = 1;
console.log(a);
}
console.log(a);
这里会报错a is not defined
,
这是由于 a 变量使用 let
声明并且在代码块中。在代码块的外部是访问不到 a的。
更本质的讲,外部的执行上下文 与 代码块的执行上下文是独立的,当代码块执行完毕后,其执行上下文被释放,而外部代码的执行上问中不存在 a, 所以报错。
但是再看下面的代码:
if (true) {
function a() {}
}
console.log(a);
没报错!甚至打印出了函数a
啊?这不对啊,怎么和前面讲的不一样捏?不要急,再看下面的这段代码:
console.log(a);
if (true) {
function a() {}
}
依旧没报错!但是打印的 undefined
现在我来解释下这种 “反常的现象”
一门妥协的语言
let,const
关键字是 ES6 以后才有,但是 function
的声明一直存在。
众所周知,JS 在执行前有一个 预编译
环节:
在 ES6 之前,在预编译时,function 同 var 一样,如果声明在 {}
中,是会被预编译时加入执行上下文的。
如果你还不知道我说的预编译是怎么回事,推荐你看我的教程:执行上下文
换句话说:
a()
if(true){
function a(){
console.log("a")
}
}
上述代码是可以正常执行的(ES5,无块级作用域)
请不要尝试,会报错,因为这段代码只在 ES5 以及之前的环境里可以生效,在 ES6 后会报错
那么在 ES6 出来之前的大量的远古代码中,可能会有人这么写。如果直接更新到 ES6 环境中,可能会导致很多代码大批量报错,而且不好查找。js 为了这种兼容性做了一定的妥协:
块级作用域中的 function 声明在外部也会被提升,但不会被初始化为函数,而是 undefined
console.log(a);
if (true) {
function a() {}
}
所以你就理解了这里的输出为什么是 undefined
奇怪的同步
现在我们来解释下面这段代码:
if (true) {
function a(){
}
}
console.log(a);
JS有这样一个奇怪的特性:
当代码执行到上述代码中的第二行,也就是函数声明那一行时,会将 外部执行上下文中 a 与 块内执行上下文里的 a进行同步。 也就是说,在这一行时,外部执行上下文中的 a 被赋值为了 函数,所以外部打印的结果是 function
为了让你更加清晰,我们再来看一段代码:
if (true) {
console.log(a);
function a(){
}
a = 1;
console.log(a);
}
console.log(a)
请你尝试思考上面输出的结果, 我来公布答案:
- function
- 1
- function
开始解释:
进入块级作用域,生成该块的执行上下文,(我在执行上文教程中讲过这个流程),在预编译环节,function a
被放入执行上下文,所以执行第一个 console.log(a) 时,打印的是function
. 执行到 第3 行,外部的 a,也就是全局的执行上下文中 a 被同步为 此时块级作用域中的 a,也就是 function
, 执行到第 5 行,块级作用域的执行上下文中的 a 被改为 1
,所以第6行打印 1
,但对于外部的执行上下文并没有影响,所以第 8 行打印 function
总结
到此,我们就解释清楚了开始的问题。另外我还需要强调,本文所有的代码在 非严格模式
下,严格模式下没有兼容,会进入正常的逻辑:
"use strict"
console.log(a)
if(true){
function a(){}
}
上述代码会报错!
转载自:https://juejin.cn/post/7247404852946288699