返回

JavaScript知识点回顾(十五)——变量、作用域与内存(三)

前端

在 JavaScript 的世界里,我们常常需要处理各种各样的数据。为了更好地管理这些数据,我们需要用到一个叫做“变量”的东西,就像给数据贴上标签一样,方便我们随时取用。但是,这些标签的使用范围和存放数据的地方,也就是“作用域”和“内存”,却常常让人摸不着头脑。

我们先来说说变量的声明。在 ES6 标准出来之前,我们只能用 var 来声明变量,这就好比在整个游乐场里都能找到的玩具,任何地方的小朋友都能玩。但这样一来,很容易出现小朋友不小心把玩具弄坏或者拿错的情况。于是,ES6 标准引入了 letconst,就像在游乐场里划分了不同的区域,每个区域的玩具只能在区域内玩。let 声明的变量就像区域内的普通玩具,可以随时更换;而 const 声明的变量就像区域内的特殊玩具,一旦确定就不能再更换了。

接下来,我们聊聊作用域。JavaScript 中的作用域可以理解为变量的活动范围。全局作用域就像整个游乐场,全局变量就像放在游乐场入口处的公共玩具,所有的小朋友都能看到和使用。函数作用域就像游乐场里的某个房间,函数内部声明的变量就像房间里的玩具,只有进入房间的小朋友才能玩。块级作用域就像房间里用围栏围起来的一小块区域,块级变量就像围栏里的玩具,只有进入围栏的小朋友才能玩。

最后,我们说说内存。内存就像游乐场的仓库,用来存放各种各样的玩具。当我们声明一个变量时,就相当于在仓库里申请了一块空间来存放这个变量的值。变量的类型决定了它需要多大的空间,就像不同大小的玩具需要不同的存放空间一样。当变量不再使用时,它所占用的内存空间就会被释放,就像玩具被小朋友玩完后放回仓库一样。

为了更好地理解这些概念,我们来看一段代码:

// 全局变量,就像放在游乐场入口处的公共玩具
var globalToy = "皮球";

// 函数作用域,就像游乐场里的某个房间
function playRoom() {
  // 函数作用域变量,就像房间里的玩具
  var roomToy = "积木";

  // 块级作用域,就像房间里用围栏围起来的一小块区域
  {
    let blockToy = "小汽车";
    console.log(globalToy); // 可以访问全局变量,输出 "皮球"
    console.log(roomToy); // 可以访问函数作用域变量,输出 "积木"
    console.log(blockToy); // 可以访问块级作用域变量,输出 "小汽车"
  }

  console.log(globalToy); // 可以访问全局变量,输出 "皮球"
  console.log(roomToy); // 可以访问函数作用域变量,输出 "积木"
  // console.log(blockToy); // 无法访问块级作用域变量,会报错
}

playRoom();

console.log(globalToy); // 可以访问全局变量,输出 "皮球"
// console.log(roomToy); // 无法访问函数作用域变量,会报错
// console.log(blockToy); // 无法访问块级作用域变量,会报错

这段代码演示了变量的作用域和内存分配情况。全局变量 globalToy 在整个程序中都是可见的。函数作用域变量 roomToy 只能在函数 playRoom 内部访问。块级作用域变量 blockToy 只能在花括号括起来的代码块内部访问。当函数执行完毕后,函数作用域变量和块级作用域变量所占用的内存空间就会被释放。

常见问题解答

1. varletconst 有什么区别?

var 声明的变量是函数作用域或全局作用域,可以重复声明和赋值;let 声明的变量是块级作用域,可以重复赋值但不能重复声明;const 声明的变量是块级作用域,必须在声明时初始化,且不能重复声明和赋值。

2. 什么是作用域链?

当代码需要访问一个变量时,JavaScript 引擎会先在当前作用域查找,如果找不到,就会沿着作用域链向上查找,直到找到为止。作用域链是由当前作用域和其所有父级作用域组成的。

3. 什么是闭包?

闭包是指函数可以访问其词法作用域外的变量,即使在其词法作用域外的函数执行完毕后,这些变量仍然可以被访问。

4. 如何避免全局变量污染?

可以通过使用立即执行函数表达式 (IIFE) 或模块化来避免全局变量污染。

5. 如何优化内存使用?

可以通过及时释放不再使用的变量、避免创建过多的全局变量、使用对象池等方式来优化内存使用。