var, let 和 const

由作用域延伸到 var, let 和 const

什么是 作用域

作用域(Scope)就像一个容器,它决定了变量,函数和对象在代码中哪些地方可以被访问。简单来说,就是变量的"有效范围"和"生存空间"。

JavaScript 中有两种基本的作用域类型:

全局 作用域:任何地方都可以访问。

局部 作用域:只能在特定区域内访问。又可以细分为函数作用域和块级作用域。

深入理解函数 作用域 和块级作用域

函数 作用域:在ES6以前,JavaScript 只有全局作用域和函数作用域。这意外着用 var 声明的变量,它的作用域被限定在它所在的函数内部。

示例如下:

javascript 复制代码
function greet() {
  var message = "Hello, world!";
  console.log(message); // 可以在函数内部访问
}

greet(); // 输出: "Hello, world!"
console.log(message); // 报错: ReferenceError: message is not defined

为什么会报错?

因为 message 变量被声明在函数内部,作用域仅限于函数。一旦函数被执行完毕,变量就会被销毁,在函数外部就无法访问了。这就是函数作用域。

块级 作用域

块指的是任何有 {} 包裹的代码。比如if语句,for循环,while循环。

在ES6引入 let 和 const 之后,JavaScript 增加了块级作用域。这意味着用 let 或 const 声明的变量,它的作用域被限制在 {} 内部。

示例如下:

javascript 复制代码
if (true) {
  let greeting = "Hello again!";
  console.log(greeting); // 可以在块内部访问
}

console.log(greeting); // 报错: ReferenceError: greeting is not defined

为什么报错?

因为 greeting 变量是用 let 声明的,它只存在于 if 语句的 {} 块中。一旦跳出这个块,它就无法被访问了。这就是块级 作用域

Var, let 和 const 的演变

理解了作用域,就很容易理解 var, let 和 const 的区别了。

首先上个示例:

ini 复制代码
function myFunction() {
  var a = 1;
  let b = 2;

  if (true) {
    var a = 3; // 这里的 a 覆盖了上面的 var a
    let b = 4; // 这是一个新的变量,只在 if 块内有效

    console.log(a); // 3
    console.log(b); // 4
  }

  console.log(a); // 3 (var 变量在 if 块内被修改了)
  console.log(b); // 2 (let 变量只在 if 块内有效,外部访问的是原来的 b)
}

myFunction();

这个例子就很好的说明了 var 是函数作用域,而 let 和 const 是块级作用域。

因为 var a 在if块里被永久地改变了,而 let b 在if块里重新声明了一个变量,并不影响if块以外的变量。

而 const 与 let 的行为几乎一模一样,唯一不同的是 const 声明的变量必须初始化,并且不能被重新赋值。那是怎么不能被重新赋值的呢?

Const 声明的基本类型变量的值是不能改变的,但如果声明的是一个引用类型(如对象和数组),那么引用本身是不能改变的,但引用指向的对象内部的属性是可以修改的。

ini 复制代码
const person = {
  name: "David",
  age: 25
};

person.age = 26; // 这没问题,修改了对象的属性
console.log(person.age); // 输出: 26

// person = { name: "John" }; // 报错!你不能把 person 重新赋值给一个新的对象

那么,Var, let 和 const 三者对比如下:

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
重复声明 允许 不允许 不允许
是否可以修改 可以 可以 不可以(引用类型除外)
变量提升 提升并初始化为undefined 提升,但有"暂时性死区" 提升,但有"暂时性死区"

从 V8 引擎的角度深入理解 var, let 和 const

让我们从 V8 引擎的角度,也就是 JavaScript 引擎是如何处理 varletconst 的,来深入理解它们之间的区别。这就像是揭开表面现象,看看底层代码到底发生了什么。

编译阶段 vs 执行阶段

要理解 V8,我们首先要明白一个关键概念:JavaScript 代码在运行前会经过一个编译阶段。

  1. 编译阶段:V8 引擎会解析你的代码,在这个阶段,它会识别所有的变量声明( var, let 和 const ),并为它们分配内存空间。
  2. 执行阶段:引擎会一行一行地执行代码,进行变量赋值和函数调用等操作。

而 var, let 和 const 的主要区别,就体现在编译阶段的内存分配和处理方式上。

V8 眼中的 var:函数作用域与 "旧时代"的遗留

当 V8 遇到 var 时,它会在编译阶段做两件事:

  1. 为变量声明分配 内存:V8会在当前函数作用域或全局作用域的内存空间中,为 var 变量创建一个槽位
  2. 立即初始化为 undefined:这个槽位会被立即赋予 undefined 值。

这就是我们常常说的变量提升(hosting)。在执行阶段,无论 var 声明出现在代码的哪个位置,所对应的内存空间都已经准备好了,知识里面的值是 undefined。

arduino 复制代码
// 编译阶段:
// V8 在当前作用域创建一个名为 myVar 的变量,并赋值为 undefined。
// var myVar = undefined; 

// 执行阶段:
console.log(myVar); // 此时 myVar 已经存在,值为 undefined
myVar = 10; // 执行赋值操作

V8 眼中的 let 和 const:块级作用域与"暂时性死区"

V8处理 let 和 const 的方式完全不同,这正是 ES6 的核心改进。当 V8 遇到 let 或 const 时:

  1. 为变量分配内存:V8 同样会在编译阶段为 letconst 变量分配内存空间。
  2. 不进行初始化:关键的区别在于 V8 不会像 var 那样将那些变量初始化为 undefined。相反,会将这些变量放置在一个"暂时性死区(Temporal Dead Zone, TDZ)"。

暂时性死区就像一个"隔离区",代码执行到 let 或 const 声明的那以后之前,任何对变量的访问都会触发一个 ReferenceError。只有执行到声明语句时,变量才会被初始化,并从暂时性死区中解封。

因此暂时性死区的存在是为了强制开发者在声明变量后再使用它,避免了 var 的意外行为。

arduino 复制代码
// 编译阶段:
// V8 在当前块级作用域创建一个名为 myLet 的变量,
// 但不给它赋值,并把它放入 TDZ。
// myLet <处于未初始化状态>

// 执行阶段:
console.log(myLet); // 引擎检查 myLet,发现它在 TDZ 中,立即抛出 ReferenceError
myLet = 20; // 永远无法到达这里

从 V8 的角度看,letconst 并不是简单地"没有变量提升",而是它们的变量提升和初始化过程被设计得更加严格和安全。这种底层处理方式的差异,是它们在行为上完全不同的根本原因。

谈谈最近学习的感想

最近 kitty 同学马上秋招了,秋招面试对于底层的理解要求是至关重要的,同时 Kitty 同学在秋招准备的路上不由得感慨,对于理论也好,实践也罢,注重对于细节的把控是必不可少的。对于同样学习的内容,该怎么把这一块学习的价值发挥到最大呢,那就和 Kitty 同学一起学习吧~

相关推荐
huabuyu7 小时前
Taro微信小程序高性能无限下拉列表实现
前端
LeeZhao@7 小时前
【项目】多模态RAG—本地部署MinerU实现多类文档解析
人工智能·面试·aigc·agi
程序员猫哥8 小时前
# Vue3响应式系统深度解析:从Proxy到effect的完整工作流揭秘
javascript
DevRen8 小时前
实现Google原生PIN码锁屏密码效果
android·前端·kotlin
ZSQA8 小时前
mac安装Homebrew解决网络问题
前端
在未来等你8 小时前
Elasticsearch面试精讲 Day 3:分片与副本策略详解
大数据·分布式·elasticsearch·搜索引擎·面试
烽学长8 小时前
(附源码)基于Vue的教师档案管理系统的设计与实现
前端·javascript·vue.js
前端一课8 小时前
前端监控 SDK,支持页面访问、性能监控、错误追踪、用户行为和网络请求监控
前端
lee5768 小时前
UniApp + SignalR + Asp.net Core 做一个聊天IM,含emoji 表情包
前端·vue.js·typescript·c#