作用域


# 作用域

# 作用域定义

作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。

通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。

# 作用域的分类

JavaScript 一共有三种作用域:

  • 全局作用域
  • 函数作用域
  • 块级作用域

# 全局作用域和函数作用域

在 ES6 之前,ES 的作用域只有两种:全局作用域和函数作用域。(ES3 开始,try /catch 分句结构和 with 中也具有块作用域,这里不讨论)

  • 全局作用域 中的对象在代码中的任何地方都能访问,其生命周期伴随着页面的生命周期。
  • 函数作用域 就是在函数内部定义的变量或者函数,并且定义的变量或者函数只能在函数内部被访问。函数执行结束之后,函数内部定义的变量会被销毁。

在 ES6 之前,JavaScript 只支持这两种作用域,相较而言,其他语言则都普遍支持块级作用域。块级作用域就是使用一对大括号包裹的一段代码,比如函数、判断语句、循环语句,甚至单独的一个 {} 都可以被看作是一个块级作用域。

简单来讲,如果一种语言支持块级作用域,那么其代码块内部定义的变量在代码块外部是访问不到的,并且等该代码块中的代码执行完成之后,代码块中定义的变量会被销毁。

遗憾的是,JavaScript 在 ES6 之前是不支持块级作用域的。没有块级作用域,对作者当时设计 JavaScript 来说会比较简单快速,但这也直接导致了「变量提升」的问题。

# 块级作用域

为了解决变量提升带来的一系列问题,ES6 引入了 letconst 关键字,从而使 JavaScript 也能像其他语言一样拥有块级作用域

需要注意的是,只有 letconst + 大括号({})才能构成块级作用域,否则单纯的大括号({})只是用作代码分割,让代码阅读起来更简单轻快一点,纯粹代码维护上的需求。

通过 let 或者 const 声明的变量会在进入块级作用域的时候被创建,但是在该变量没有赋值之前,引用该变量 JavaScript 引擎会抛出错误,这就是「暂时性死区」。

# 词法作用域

词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。(标识符:在 JS 中所有可以由我们自主命名的都可以称为是标识符,例如:变量名、函数名、属性名都属于标识符)
—— 出自《你不知道的JavaScript(上)》

# 作用域链

作用域链也称词法作用域链,顾名思义,它跟词法作用域有关。

作用域链本质上就是查找变量的链条(确定变量来自于哪里,变量是否可以访问,或者说,确定一个变量来自于哪个作用域)。

查找作用域链的步骤如下:

  • 查看当前作用域,如果当前作用域声明了这个变量,就确定结果。
  • 查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明。
  • 再查找上级函数的上级函数,直到全局作用域为止。
  • 如果全局作用域中也没有,我们就认为这个变量未声明(抛出异常:xxx is not defined)。

重点:词法作用域是代码阶段就决定好的,和函数是怎么调用的没有关系

举个例子:

function innerFunction() {
    console.log(myName);
}
function outerFunction() {
    var myName = "函数作用域";
    innerFunction();
}

var myName = "全局作用域";
outerFunction();
1
2
3
4
5
6
7
8
9
10

从代码中可以看出,全局执行上下文和 outerFunction 函数的执行上下文中都包含变量 myName,那 innerFunction 函数里面 myName 的值到底该选择哪个?

根据上面提到的查找顺序,如下:

  • 查看当前作用域,innerFunction 函数里面不存在变量 myName
  • innerFunction 函数中使用了外部变量,向上级查找。
  • 重点来了,由于 innerFunction 函数里面不存在变量 myName ,此时 JavaScript 引擎会去全局执行上下文中查找,而不是它的调用方 outerFunction 函数的执行上下文。这是因为在 JavaScript 执行过程中,其作用域链是由词法作用域决定的,而词法作用域是代码阶段就决定好的,和函数是怎么调用的没有关系

既然如此,根据词法作用域,outerFunctioninnerFunction 的上级作用域都是全局作用域,所以如果这两个函数使用了一个它们没有定义的变量,那么它们会到全局作用域去查找。

(完)