JavaScript 有两种主要的作用域,即全局作用域和函数作用域。这些作用域决定了变量在代码中的可见性和生命周期。此外,ES6 引入了块级作用域,允许使用 let 和 const 声明的变量在声明它们的块内部可见,而不仅限于函数内部。块级作用域的变量只在块内部可见。
全局作用域
全局作用域是整个 JavaScript 程序的顶层作用域。在全局作用域中声明的变量可以在代码的任何地方被访问,包括函数内部。这意味着全局作用域的变量是全局变量,对整个程序可见。
|
|
在上述例子中,变量 globalVar 作为全局变量可以在函数 foo 中访问,也可以在函数的外部访问此全局变量。
函数作用域
函数作用域是在函数内部声明的作用域。在函数作用域中声明的变量只能在该函数内部访问,外部作用域无法访问。每次调用函数都会创建一个新的函数作用域。
|
|
在上述例子中,变量 localVar 作为定义在函数 foo 内的局部变量是无法在函数的外部去访问此变量的。
块级作用域
块级作用域是指在代码块内部声明的变量只在该块内部可见,而在块外部无法访问。块可以是由一对花括号 {} 包围的语句块,例如 if 语句、for 循环、函数内部,或任何其他代码块。
块级作用域的概念在 ES6 中引入,主要通过 let 和 const 关键字来声明变量。这与传统的 var 关键字声明的变量不同,后者具有函数级作用域或全局作用域。
|
|
如果你用 var 关键字声明变量,那么该变量将具有函数级作用域或全局作用域,而不是块级作用域。
|
|
即使你的变量声明在代码块内部,但如果使用 var 关键字声明,那么该变量将具有函数级作用域或全局作用域,而不是块级作用域。这就是 var 的变量提升:
|
|
上述代码等同于以下代码:
|
|
闭包
词法作用域
在理解闭包之前,我们需要先了解词法作用域。词法作用域(Lexical Scope),也称为静态作用域,是一种用于确定变量访问规则的作用域规则。在词法作用域中,一个变量的作用域是在代码编写时就确定的,而不是在运行时动态确定的。
在词法作用域中,变量的可见性是由变量在代码中的位置来决定的,通常是由变量的声明位置决定的。具体来说,一个变量在函数内部声明,那么它的作用域将限定在这个函数内部,而函数外部无法访问它。
|
|
我们可以说变量 globalVar 的词法作用域是全局作用域,而变量 localVar 的词法作用域是函数作用域。
闭包的定义
闭包(Closure) 是指一个函数可以访问并操作其词法作用域之外的变量的能力。换句话说,闭包是指一个函数可以访问其词法作用域之外的变量,它可以访问并且“记住”其创建时所在的作用域,即使该函数在其词法作用域之外执行。
|
|
举个例子,闭包相当于是一个背包,你可以将任意物品(变量)放入这个背包,当你关上背包后(离开当前词法作用域)只有等再次打开(调用闭包)才能拿到里面的物品。
函数 bar 定义在函数 foo 的内部,并且它的词法作用域与变量 localVar 重叠。当函数 foo 返回函数 bar 时,函数 bar 仍然可以访问变量 localVar,尽管函数 foo 已经执行完毕并且其词法作用域已经销毁。