了解JavaScript中的变量作用域

来自菜鸟教程
跳转至:导航、​搜索

范围是变量或函数的执行上下文。 它定义了它可以访问的数据。 这个概念可能看起来很简单,但有一些重要的微妙之处。

JavaScript 传统上有两种作用域:全局作用域和局部作用域。 作用域的目的是提供对执行上下文可以访问的所有变量和函数的访问。

全球范围

当一个变量在任何函数之外声明时,它自动属于全局范围,并且可以从程序中的任何地方访问,无论是函数还是任何块。 此外,如果需要,在浏览器中,我们可以通过在程序中的任何位置将其声明为 window.newVariable 来创建一个全局变量。

const nestAnimal = 'crocodilian';   // belongs to the Global scope

function getNestInfo(){
  window.eggs = 5;     // as well belongs to the Global scope
}

实际上,在浏览器中,全局范围内的变量属于全局window对象。

JavaScript 是一种垃圾收集语言,它在上下文中执行程序时保持所有变量可用并在之后删除。 让我们考虑变量的生命周期。 该变量在函数执行期间出现。 变量在函数内部使用,然后函数结束。 此时不再需要这个变量,所以它的内存可以被回收,JavaScript 会从内存中删除这个变量。 但是全局变量在应用程序运行的所有时间都保留在内存中并阻塞它,从而减慢程序速度,还可能导致意外的名称冲突。

这一切都意味着,只要有可能,您应该避免定义全局变量。 只有在非常特殊的情况下才真正需要它们,所以要小心。

本地范围

ES6 使用 const 和 let 关键字 引入了块范围变量。 使用这些关键字,本地范围被创建并存在于围绕它的最里面的块中。 它可以是一个函数、for 循环、while 块、if 块等。 这种局部范围的变量只能从该块内访问。

每个块都有自己的执行上下文,它定义了它可以访问哪些数据,以及它应该如何表现。 在上下文中执行代码时,会创建一个作用域链。 它包括该块内所有声明的变量和函数,然后是来自包含(父)上下文的数据,等等。 这种模式一直持续到达到全局上下文。

让我们看一个例子:

let caymanMood = 'calm';

function changeMood(newMood){
  if (caymanMood === 'calm'){
    caymanMood = newMood;
  } else {
    caymanMood = 'calm';
  }
}

changeMood('happy');

函数 changeMood 有一个作用域链,其中包含两个对象:它自己的变量对象(参数对象 newMood)和全局上下文的变量对象 caymanMood。 该函数可以访问 caymanMood 因为它是其作用域链的一部分。

范围链增强

除了全局和本地执行上下文之外,还可以增加作用域链。 我们可以通过两种方式做到这一点。

  • 点1:A with 语句
  • Point 2try...catch 语句中的 catch 块。
function buildNest() {
  const assets = 'grass';
  with(reptilian){
    const building = ability + assets;
  }
  return building;
}

with 创建一个添加到作用域链前面的对象,但问题是当您阅读代码时,您无法确定究竟哪个对象会被修改。 是全局变量 ability 还是上下文中的变量 reptilian.ability。 所以不能保证程序的正确执行。 MDN 网络文档 不建议使用 with 语句,因为它可能会导致混淆错误和兼容性问题。

catch 语句创建一个新的变量对象,其中包含抛出的错误对象的声明,并且这个错误对象被添加到作用域链的前面。

包起来

有了这个,您现在应该对 JavaScript 中本地和全局范围的工作方式有了更好的理解,以及如何尽可能依赖最接近的本地上下文是编写易于阅读和维护的代码的好主意。