JavaScript 作用域
Tip
参考:Todd Motto 撰写的入门材料。
作用域是限制访问变量或函数的区域。在 JacvaScript 中变量有三种作用域 Scope:
- 全局作用域:所有函数之外定义的标识符,可以在程序中任何位置被访问,即全局可用
- 函数作用域:在函数内部定义的标识符,仅可在该函数内部的所有位置可见。使用函数名称或变量名称定义函数时,函数内部也可以引用该函数或变量名称,即自引用。
- 块作用域(默认不含有块作用域):使用关键字
var
在代码块中,如if
或循环代码块(代码块用花括号{ }
表示)中,创建的变量可以在块代码之外访问,即不会该变量不会创建块作用域。可以使用let
和const
(而非var
)声明变量,将变量作用域限制到块,从而避免在函数中的内嵌代码块内声明的变量被提升到函数作用域顶部。
- 提升 hoisting 是浏览器解析 JavaScript 的结果,即在执行任何 JavaScript 代码之前,所有变量或函数的声明都会被「提升」到其作用域的顶部,这改变了声明的位置和顺序(而 JavaScript 运行代码是依次从上到下执行的),例如在提升变量时只是提升了变量的声明,而赋值语句未被提升,可能会造成难以理解的输出。为了避免提升可使用
let
或const
定义变量。同时为保证语法和行为相互一致,请在脚本的顶部声明函数、在函数顶部声明变量。 - 函数定义的不同方式,其提升的方式不同
- 使用
function functionName()
语法声明函数时,函数声明和定义(函数里的实际说明)同时提升到作用域的顶部 - 使用变量
var functionName = function()
定义匿名函数时,只有函数的声明(例如var functionName
)提升到作用域的顶部
- 使用
块级作用域
在 JavaScript 的代码块,如 if 语句或循环语句块中,使用关键字 var
创建的变量不包含块级作用域,与 Python 语法不同,在代码块中创建的变量,在代码块外部仍可访问。若需要将变量作用域限制在块,可使用关键字 let
或 const
创建变量
js
var outsideExample = "First string";
if (true) {
var outsideExample = "Second string";
console.log(outsideExample);
}
console.log(outsideExample);
🔨 输出结果
js
> Second string
> Second string
覆盖
全局作用域的变量在函数中进行了修改,会覆盖掉全局作用域定义的变量值。若要避免全局作用域的变量出现覆盖/遮蔽,可在函数中进行重新声明变量(使用 var
关键字),以创建一个仅作用于函数作用域的变量(即时该变量名称与全局作用域中定义的名称一致),而未使用关键字 var
创建并赋值的变量,其作用域大于函数作用域,当函数外部存在同名变量,可能因此引起覆盖或其他异常。
js
var outsideExample = "First string";
function example() {
var outsideExample = "Second string";
}
example();
console.log(outsideExample);
🔨 输出结果
js
> First string
js
var outsideExample = "First string";
function example() {
outsideExample = "Second string";
}
example();
console.log(outsideExample);
🔨 输出结果
js
> Second string
闭包
指通过特定的方式,在函数运行结束后,在其中创建的变量或函数(作用域)仍可(通过外部)访问。保留函数(或变量)访问权有多种方式:
- 将在该作用域创建的变量或函数传递到函数
setTimeout
- 将在该作用域创建的函数或变量作为返回值
- 将在该作用域创建的函数或变量保存(赋值)到全局变量中
Tip
闭包将函数内创建的属性或方法赋值给全局变量,此时储存的值在函数运行结束后仍然可以访问,但是如果函数再次运行则此时的全局变量指向的值(在函数内部创建的属性或方法)则会改变。由于每次调用函数都会生成一个新的临时函数作用域,里面的属性和方法也是重新创建。