JavaScript知识点回顾(十五)——变量、作用域与内存(三)
2024-02-24 15:55:54
在 JavaScript 的世界里,我们常常需要处理各种各样的数据。为了更好地管理这些数据,我们需要用到一个叫做“变量”的东西,就像给数据贴上标签一样,方便我们随时取用。但是,这些标签的使用范围和存放数据的地方,也就是“作用域”和“内存”,却常常让人摸不着头脑。
我们先来说说变量的声明。在 ES6 标准出来之前,我们只能用 var
来声明变量,这就好比在整个游乐场里都能找到的玩具,任何地方的小朋友都能玩。但这样一来,很容易出现小朋友不小心把玩具弄坏或者拿错的情况。于是,ES6 标准引入了 let
和 const
,就像在游乐场里划分了不同的区域,每个区域的玩具只能在区域内玩。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. var
、let
和 const
有什么区别?
var
声明的变量是函数作用域或全局作用域,可以重复声明和赋值;let
声明的变量是块级作用域,可以重复赋值但不能重复声明;const
声明的变量是块级作用域,必须在声明时初始化,且不能重复声明和赋值。
2. 什么是作用域链?
当代码需要访问一个变量时,JavaScript 引擎会先在当前作用域查找,如果找不到,就会沿着作用域链向上查找,直到找到为止。作用域链是由当前作用域和其所有父级作用域组成的。
3. 什么是闭包?
闭包是指函数可以访问其词法作用域外的变量,即使在其词法作用域外的函数执行完毕后,这些变量仍然可以被访问。
4. 如何避免全局变量污染?
可以通过使用立即执行函数表达式 (IIFE) 或模块化来避免全局变量污染。
5. 如何优化内存使用?
可以通过及时释放不再使用的变量、避免创建过多的全局变量、使用对象池等方式来优化内存使用。