全部 / 前端 / 技术 · 2022年4月28日 0

12 – JavaScript 中的作用域

广告位招租 (vx: ghostcode, 备注:网站广告)

原文地址:https://dev.to/bhagatparwinder/scope-in-javascript-3jim

JavaScript 中的作用域规定了一个变量或函数的可用范围。根据变量或函数的声明位置它们或许只能在个别 JavaScript 代码块中可用在其他地方则不行。我们在 JavaScript 中会接触到三种作用域。

Global Scope

当一个变量/方法在 script 或 JS 文件中(不属于任何一个函数)声明 并且可以那个文件或者 HTML 文档中引用到,它就是被声明在了全局作用域。它不属于某个方法或模块。

在浏览器中,有一个全局变量叫 window,你可能会看到开发者经常把 window.setIntervalsetInterval 交换使用而没有带来后果或副作用,那是因为 setInterval 在全局作用域中。浏览器在 window 对象上还提供了许多其他的全局方法。

声明变量的方法有三种:constletvar,它们都可以声明全局变量,但只有 var 声明变量挂在到了 window 上。

Function or Local Scope

当在一个函数中声明一个变量时,它只能在函数中使用外面无法使用。因为它的作用域只归属于函数。

const first = "Parwinder"

function sayLastName() {
    const last = "Bhagat";
}

console.log(first); // Parwinder
console.log(last); // Reference Error: last is not defined

正如你所见,last 在函数外面是无法使用的,在 sayLastName 中是可以使用的。

const first = "Parwinder"

function sayLastName() {
    const last = "Bhagat";
    console.log(last); // Bhagat
}

sayLastName(); // Remember, I have to call a function to execute the code block in it.

console.log(first); // Parwinder

我们可以读取全局变量并在任何地方使用,同样使用上面的例子:

const first = "Parwinder"

function sayLastName() {
    console.log(first); // Parwinder
    const last = "Bhagat";
    console.log(last); // Bhagat
}

sayLastName();

console.log(first); // Parwinder

即使我不能在函数外中获取 last ,但依旧可以在函数中获取全局变量 first 。这是因为在 JavaScript 中当解释器读取一个变量时,首先在当前作用域中查找,若没有则会向上层作用域查找。在这个例子中,当我们在 sayLastName 中打印 first 时,JS 引擎首先会在函数作用域中查找,之后在全局作用域中查找。若任何作用域中都没有找到,则会报一个引用的错误。

Block Scope

块级作用域是被定义在一对大括号中的。根据定义函数是一个快,但函数中还有更小的快。函数中的 for loopif 语句都有它们自己的块级作用域。块级作用域帮助我们在代码中组织了很小的作用域。

创建块级作用域方法是使用 const 或 let 在大括号中声明变量。

function amICool(name) {
    let attribute;
    if (name === "Parwinder") {
        attribute = "cool"
    }
    return attribute;
}

console.log(amICool("Parwinder")); // cool
console.log(amICool("Bhagat")); // undefined because the name was not "Parwinder"

上面的例子是为 attribute 创建了函数作用域,我们可以把它缩小些:

function amICool(name) {
    if (name === "Parwinder") {
       let attribute = "cool"
    }
    return attribute; // Reference error: attribute is not defined
}

console.log(amICool("Parwinder")); // Reference error: attribute is not defined
console.log(amICool("Bhagat")); // This will not run as we ran into an error with the first execution of the function

发生了什么?let 使 attribute 变量属于块级作用域,此时它被限制在 if 语句中。当我们尝试在 if 语句外返回它时,attribute 是找不到的。

一个经验:首先使用 const 来声明变量,若变量的值会变动,则使用 let 次之,var 是最后的选择。

Lexical Scoping

等等,这是什么?不是说只有三种作用域吗?词法作用域是 JavaScript 中使用的作用域模型。变量或函数的作用域是它们被定义时所在的作用域。

  • 词法作用域又叫静态作用域。
  • 一个声明 被调用时的作用域 不一定是它的词法作用域。相反的,定义时的作用域 才是词法作用域
const myName = "Parwinder";

function sayMyName() {
    console.log(myName);
}

function sayMyNameAgain() {
    const myName = "Bhagat";
    sayMyName();
}

sayMyNameAgain();

输出 Parwinder ,为什么:

  1. sayMyNameAgain 执行时,内部调用了 sayMyName。
  2. 即使 sayMyNameAgain 内部有一个 myName 的变量,但它不会被打印。
  3. sayMyNameAgain 打印全局变量 myName 的值:Parwinder。

因为 sayMyName 是在声明它的作用域下执行的。