在编程语言中都有作用域这个概念,在ES6之前,JS只有全局作用域和函数作用域两种,因为这种设计满足不了开发的需求,在ES6的时候添加了块级作用域{},并增加了两个关键字let,const用来替代var
var声明的变量会在执行上下文的变量环境中存储,而ES6新增的let和const定义的变量是存储在执行上下文的词法环境中的,而块级作用域中的变量又会在词法作用域中在单独存储一个区域。
而每个执行上下文的变量环境中,都包含了一个外部引用,用来指向外部的执行上下文,我们把这个外部引用称为outer。作用域链就是这样来的
我们在获取变量的值的时候会先去当前执行上下文中查询,没有命中的话就去outer指向的执行上下文中查询,以此类推,直至全局的执行上下文
那么关键就是这个outer指向的执行上下文是哪个:它是有词法解析阶段决定的,也就是说是所处的代码位置决定着outer指向的执行上下文是哪个,和调用的地方没有关系
看个例子:
function foo() {
console.log(name)
}
function bar() {
let name = '777'
foo()
}
let name = 'zmheang'
bar()
// 按照常规应该是输出777,但是结果却是zmheang
// 这就是因为foo()和bar()的词法作用域都是全局执行上下文,所以在fo o()中没找到name变量,就会找outer指向的执行上下文,于是就是zmheang
词法作用域:就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它能够预测代码在执行过程中如何查找标识符
再看个例子:
function f1() {
var name = 'zmheang'
let test1 = 1
if (1) {
let name = '777'
console.log(test)
}
}
function f2() {
var name = 'zm'
let test = 2
{
let test = 3
f1()
}
}
var name = 'meng'
let age = 26
let test = 1
f2()
// 同理,f1()本身的执行上下文没有test,于是就去outer指向的执行上下文中去找,全局中的是test=1,于是输出1