作用域

作用域(scope)是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。

在JavaScript中,有以下几种作用域:

  1. 全局作用域(Global Scope):全局作用域是在整个代码中都可访问的范围,它定义了在程序的顶层定义的变量和函数。
  2. 函数作用域(Function Scope):函数作用域是指在函数内部定义的变量的可见范围。这意味着在函数内部声明的变量只能在函数内部访问。
  3. 块级作用域(Block Scope):块级作用域是指由代码块(通常由花括号 {} 包围)创建的作用域。在 ES6 之前,JavaScript中没有块级作用域,只有全局作用域和函数作用域。但是,在 ES6 中引入了let和const关键字,它们引入了块级作用域。

作用域链

作用域可以形成嵌套关系,内部作用域可以访问外部作用域中的变量,但外部作用域无法访问内部作用域中的变量。这种嵌套的作用域关系称为作用域链(Scope Chain)。

每个执行上下文的变量环境中,都包含一个外部引用(outer),用来指向外部的执行上下文。当一段代码使用了一个变量时,JavaScript 引擎首先会在当前执行上下文中查找该变量,如果没有找到 JavaScript 引擎会继续在 outer 所指的执行上下文中查找。

下面据个例子来展示作用域链:

function bar() {
    console.log(myName)
}
function foo() {
    var myName = "极客邦"
    bar()
}
var myName = "极客时间"
foo()

Pasted image 20240321151755

barouter 为什么指向全局执行上下文而不是 foo 的执行上下文?这是因为在 JavaScript 执行过程中,其作用域链是由词法作用域决定的。

词法作用域

词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。词法作用域是代码编译阶段就决定好的,和函数如何调用没有任何关系。

Pasted image 20240321155543

上图中整个词法作用域链的顺序是:foo 函数作用域—>bar 函数作用域—>main 函数作用域—> 全局作用域。

闭包

在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。比如外部函数是 foo,那么这些变量的集合就称为 foo 函数的闭包。