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
已经执行完毕并且其词法作用域已经销毁。