在本教程中,您将了解决定变量可见性和可访问性的 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, letconst 关键字的 counter 变量,然后将其返回。

在函数之外,我们调用 getCounter() 函数并在控制台中打印结果。这个问题被称为全局变量的泄漏。

在幕后,JavaScript 引擎首先在 getCounter() 函数的本地作用域内查找  counter 变量。

因为没有 varletconst 关键字,所以 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 提供 letconst 关键字,允许您在块范围内声明变量。

通常,只要你看到花括号 {},它就是一个块。它可以是 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 变量作用域,包括函数作用域、全局作用域和块作用域。