04 关于“块级”作用域

80 阅读3分钟

“块级”作用域小例子

首先看一下下面这段代码输出什么:

var a
if (true) {
    a = 1
    function a() {}
    function b() {}
    a = 2
    console.log(a)
}
console.log(a)
console.log(b)

输出结果:

2
1
[Function: b]

在第二行 if (true) 那里打个端点调试一下:

代码运行完第1行时:
image.png 现象:声明了两个变量:ab ,它们的值都是 undefined
分析:a 变量使用了 var 进行了显式声明; b 变量由 {} 代码块中的函数声明提升到了顶部

代码运行完第2行时:
image.png 现象:全局作用域的变量 ab 值并没有变化;新建了一个“块级”作用域,其中包含两个变量 ab,并且值都为函数
分析:{}包裹的代码块将创建“块级”作用域,代码块中的函数声明被提升到了块顶并被初始化为对应的函数
*我们发现到代码块中其实还初始化了一个 this ,它的值是一个空对象 {} *

代码运行完第3行时:

image.png 现象:全局作用域的变量 ab 值并没有变化;“块级”作用域的变量 a 被重新赋值为 1
分析:第2行代码对变量a重新赋值,就涉及从作用域链中查找变量a,显然首先从“块级”作用域中找到了变量 a ,则对其进行了赋值操作

代码运行完第4行时:

image.png 现象:全局作用域的变量 a 被重新赋值为 1
分析:这一行很诡异,因为它改变了全局作用域变量 a 的值

代码运行完第5行时:
image.png 现象:全局作用域的变量 b 被重新赋值为 函数b
分析:这一行可以理解为被提升到全局作用域的变量 b 进行了初始化

代码运行完第6行时:

image.png 现象:“块级”作用域的变量 a 被重新赋值为 2
分析:第6行代码对变量a重新赋值,就涉及从作用域链中查找变量a,显然首先从“块级”作用域中找到了变量 a ,则对其进行了赋值操作

第7行打印变量a,是从“块级”作用域中查找到的,打印 '2',执行完此行后块中的代码执行完毕,则“块级”作用域被销毁

第8行打印变量a,是从全局作用域中查找到的,打印 '1'

第9行打印变量b,是从全局作用域中查找到的,打印 '函数b'

例子的几种变化

再看一下下面几个例子:

eg1:

var a;
if (true) {
    // a = 1
    function a() {}
    function b() {}
    a = 2;
    console.log(a);
}
console.log(a);
console.log(b);

输出:

2
[Function: a]
[Function: b]

eg2:

var a;

console.log(a)
console.log(b)

if (true) {
    // a = 1
    function a() {}
    function b() {}
    a = 2;
    console.log(a);
}

console.log(a);
console.log(b);

输出:

undefined
undefined
2
[Function: a]
[Function: b]

eg3:

var a;

console.log(a);
console.log(b);

function a() {}
function b() {}

a = 2;

console.log(a);
console.log(b);

输出:

[Function: a]
[Function: b]
2
[Function: b]

总结

  • 函数声明将被提升
    • 非代码块中的函数声明将被提升到外部作用域顶端,并直接被初始化为函数
    • 代码块中的函数声明也将被提升到外部作用域顶端,但不会被初始化
  • 代码块中声明的函数会被提升到外部作用域,但是被提升到外部作用域的变量值会受到代码块中变量赋值的影响

不要在代码块中声明函数