在本教程中,您将了解决定变量可见性和可访问性的 JavaScript 变量作用域。
什么是变量作用域
作用域决定一个变量的可见性和可访问性。JavaScript 具有三个作用域:
- 全局作用域
- 本地作用域
- 块级作用域(从 ES6 开始)
全局作用域
当 JavaScript 引擎执行脚本时,它会创建一个全局执行上下文。此外,它还将您在函数外部声明的变量分配给全局执行上下文。这些变量在全局范围内。它们也称为全局变量。
例如下面的示例:
var message = 'Hi';
message
变量是在全局作用域名的。它可以在脚本的任何地方访问。
本地作用域
您在函数内部声明的变量是函数的局部变量。它们被称为局部变量。例如:
var message = 'Hi';
function say() {
var message = 'Hello';
console.log(message);
}
say();
console.log(message);
输出:
Hello
Hi
当 JavaScript 引擎执行 say()
函数时,它会创建一个函数执行上下文。say()
函数内部声明的 message
变量绑定到函数的函数执行上下文,而不是全局执行上下文。
作用域链
考虑以下示例:
var message = 'Hi';
function say() {
console.log(message);
}
say();
输出:
Hi
在这个例子中,我们引用 message
变量在 say()
函数内部。在幕后,JavaScript 执行以下操作:
- 在
say()
函数的当前上下文(函数执行上下文)中查找变量message
。它找不到message
变量。 - 然后在外部的执行上下文中查找
message
变量。最后 JavaScript 引擎在全局执行上下文找到message
变量。
JavaScript 解析变量的方式是在其当前作用域查找变量,如果找不到该变量,则向上查找外部作用域,这称为作用域链。
多作用域链
考虑以下示例:
var y = 20;
function bar() {
var y = 200;
function baz() {
console.log(y);
}
baz();
}
bar();
输出:
200
在这个例子中:
- 首先,JavaScript 引擎在
baz()
函数作用域查找变量y
。但 JavaScript 它并没有找到变量y
。所以 JavaScript 引擎向外部作用域查找变量y
。 - 然后,JavaScript 引擎在
bar()
函数找到变量y
。它可以在bar()
函数范围内找到变量y
,因此 JavaScript 引擎停止搜索。
全局变量泄漏:JavaScript 的奇怪部分
请阅读下面的示例:
function getCounter() {
counter = 10;
return counter;
}
console.log(getCounter());
输出:
10
在此示例中,我们将 10 分配给不带 var
, let
或 const
关键字的 counter
变量,然后将其返回。
在函数之外,我们调用 getCounter()
函数并在控制台中打印结果。这个问题被称为全局变量的泄漏。
在幕后,JavaScript 引擎首先在 getCounter()
函数的本地作用域内查找 counter
变量。
因为没有 var
、let
或 const
关键字,所以 counter
变量在本地作用域内不可用。因为它尚未创建。
然后,JavaScript 引擎沿着作用域链在全局作用域中查找 counter
变量。全局作用域也没有 counter
变量,于是 JavaScript 引擎在全局范围内创建 counter
变量。
要修复这种奇怪的行为,您可以在脚本顶部或函数顶部使用 'use strict'
:
'use strict'
function getCounter() {
counter = 10;
return counter;
}
console.log(getCounter());
现在,代码抛出一个错误:
ReferenceError: counter is not defined
下面展示如何在函数中使用 'use strict'
:
function getCounter() {
'use strict'
counter = 10;
return counter;
}
console.log(getCounter());
块级作用域
ES6 提供 let
和 const
关键字,允许您在块范围内声明变量。
通常,只要你看到花括号 {}
,它就是一个块。它可以是 if
, else
, switch
条件或 for
, do while
, 和 while
循环中的区域。
请参阅以下示例:
function say(message) {
if(!message) {
let greeting = 'Hello'; // 块级作用域
console.log(greeting);
}
console.log(greeting); // ReferenceError
}
say();
在此示例中,我们的 console.log(greeting)
引用在 if
创建的块级作用域的greeting
变量,导致错误 ReferenceError。
结论
在本教程中,您了解 JavaScript 变量作用域,包括函数作用域、全局作用域和块作用域。