js中除了全局作用域,还有函数作用域。
嗯,还有块级作用域。
当将代码看成一个IIFE时,全局作用域其实就是一个函数作用域。
那么函数作用域跟块级作用域的区别是什么呢?
函数作用域:变量在定义的函数内及嵌套的子函数内处处可见;
块级函数域:变量在离开定义的块级代码后马上被回收。
那么为什么两者会有这样的区别呢?
因为在函数作用域内,变量声明有一个提升hoisting的过程。
图2-1
let定义的变量具有块级作用域,待会讲。
如果var定义的变量跟let定义的变量一样没有hoisting的过程,那么两者应该都是ReferenceError,但是结果发现b是undefined,说明b发生了提升,其效果如下:
图2-2
js中只有两种情况下会发生提升:声明了一个变量(如果是定义了一个变量,则分离成声明跟赋值,提取声明操作),或者定义了一个函数表达式。
图3-1
可见,单纯的声明会提升,
定义变量,则提取其声明操作提升,
函数表达式会完全提升。
那如果在提升的时候发生重命名了怎么办?
如图3-2,照之前所说的提升,实际运行前的代码应该为图3-3
那么之后的var a为什么没有覆盖掉function a(){}这个定义,使第一个打印为undefined?
因为编译器在遇到变量声明时(函数表达式可以理解为包含了声明和赋值的操作),会先查看当前作用域,如果该变量不存在,则在该作用域中声明该变量;如果存在,则会忽略该声明。所以引擎实际接收的代码应该是图3-4:
图3-4
注意不要把a跟隐式全局变量混淆。
嗯,js中还是存在块级作用域的。
图4-1,catch块捕获的异常只在catch块中可见,
但是catch块中定义的其他变量还是属于整个函数作用域的。
如图4-2,let定义的变量遵从块级作用域,不会提升,不会在整个函数域内起作用。
图4-2
const定义的变量是在let的基础上,如图4-3,增加了必须在声明时赋值为一个常量的限制,如图4-4。
图4-3
图4-4
ReferenceError:作用域判别错误,通过作用域链的搜寻找不到相应的变量。
TypeError:可以通过作用域搜索到变量,但是对变量的操作不合法。
SyntaxError:语法错误
直接在函数体内定义的函数表达式(可以看着声明和定义),整个都会提前;
但是在块中定义的函数表达式,只会提升其声明部分。如图5-1
图5-1