百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

深入理解JS:var、let、const的异同

lipiwang 2024-10-16 13:10 50 浏览 0 评论

目录

  • 序言
  • var 与 let 的区别作用域重复声明绑定全局对象变量提升与暂存死区
  • let 与 const 异同
  • 参考


1.序言

var、let 和 const 都是 JavaScript 中用来声明变量的关键字,并且 let 和 const 关键字是在 ES6 中才新增的。既然都是用来声明变量的,那它们之间有什么区别呢?让我们来一探究竟。


2.var 与 let 的区别

(1)作用域

用 var 声明的变量的作用域是它当前的执行上下文,即如果是在任何函数外面,则是全局执行上下文,如果在函数里面,则是当前函数执行上下文。换句话说,var 声明的变量的作用域只能是全局或者整个函数块的。

而 let 声明的变量的作用域则是它当前所处代码块,即它的作用域既可以是全局或者整个函数块,也可以是 if、while、switch等用{}限定的代码块。

另外,var 和 let 的作用域规则都是一样的,其声明的变量只在其声明的块或子块中可用。

示例代码:

function varTest() {
  var a = 1;

  {
    var a = 2; // 函数块中,同一个变量
    console.log(a); // 2
  }

  console.log(a); // 2
}

function letTest() {
  let a = 1;

  {
    let a = 2; // 代码块中,新的变量
    console.log(a); // 2
  }

  console.log(a); // 1
}

varTest();
letTest();

从上述示例中可以看出,let 声明的变量的作用域可以比 var 声明的变量的作用域有更小的限定范围,更具灵活。


(2)重复声明

var 允许在同一作用域中重复声明,而 let 不允许在同一作用域中重复声明,否则将抛出异常。

var 相关示例代码:

var a = 1;
var a = 2;

console.log(a) // 2

function test() {
  var a = 3;
  var a = 4;
  console.log(a) // 4
}

test()

let 相关示例代码:

if(false) {
  let a = 1;
  let a = 2; // SyntaxError: Identifier 'a' has already been declared
}
switch(index) {
  case 0:
    let a = 1;
  break;

  default:
    let a = 2; // SyntaxError: Identifier 'a' has already been declared
    break;
}

从上述示例中可以看出,let 声明的重复性检查是发生在词法分析阶段,也就是在代码正式开始执行之前就会进行检查。


(3)绑定全局对象

var 在全局环境声明变量,会在全局对象里新建一个属性,而 let 在全局环境声明变量,则不会在全局对象里新建一个属性。

示例代码:

var foo = 'global'
let bar = 'global'

console.log(this.foo) // global
console.log(this.bar) // undefined

那这里就一个疑问, let 在全局环境声明变量不在全局对象的属性中,那它是保存在哪的呢?

var foo = 'global'
let bar = 'global'

function test() {}

console.dir(test)

在Chrome浏览器的控制台中,通过执行上述代码,查看 test 函数的作用域链,其结果如图:

由上图可知,let 在全局环境声明变量 bar 保存在[[Scopes]][0]: Script这个变量对象的属性中,而[[Scopes]][1]: Global就是我们常说的全局对象。


(4)变量提升与暂存死区

var 声明变量存在变量提升,如何理解变量提升呢?

要解释清楚这个,就要涉及到执行上下文和变量对象。

在 JavaScript 代码运行时,解释执行全局代码、调用函数或使用 eval 函数执行一个字符串表达式都会创建并进入一个新的执行环境,而这个执行环境被称之为执行上下文。因此执行上下文有三类:全局执行上下文、函数执行上下文、eval 函数执行上下文。

执行上下文可以理解为一个抽象的对象,如下图:

Variable object:变量对象,用于存储被定义在执行上下文中的变量 (variables) 和函数声明 (function declarations) 。

Scope chain:作用域链,是一个对象列表 (list of objects) ,用以检索上下文代码中出现的标识符 (identifiers) 。

thisValue:this 指针,是一个与执行上下文相关的特殊对象,也被称之为上下文对象。


一个执行上下文的生命周期可以分为三个阶段:创建、执行、释放。如下图:

而所有使用 var 声明的变量都会在执行上下文的创建阶段时作为变量对象的属性被创建并初始化,这样才能保证在执行阶段能通过标识符在变量对象里找到对应变量进行赋值操作等。

而用 var 声明的变量构建变量对象时进行的操作如下:

  • 由名称和对应值(undefined)组成一个变量对象的属性被创建(创建并初始化)
  • 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。

上述过程就是我们所谓的“变量提升”,这也就能解释为什么变量可以在声明之前使用,因为使用是在执行阶段,而在此之前的创建阶段就已经将声明的变量添加到了变量对象中,所以执行阶段通过标识符可以在变量对象中查找到,也就不会报错。

示例代码:

console.log(a) // undefined

var a = 1;

console.log(a) // 1


let 声明变量存在暂存死区,如何理解暂存死区呢?

其实 let 也存在与 var 类似的“变量提升”过程,但与 var 不同的是其在执行上下文的创建阶段,只会创建变量而不会被初始化(undefined),并且 ES6 规定了其初始化过程是在执行上下文的执行阶段(即直到它们的定义被执行时才初始化),使用未被初始化的变量将会报错。

let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

在变量初始化前访问该变量会导致 ReferenceError,因此从进入作用域创建变量,到变量开始可被访问的一段时间(过程),就称为暂存死区(Temporal Dead Zone)。

示例代码 1:

console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined

var bar = 1;
let foo = 2;

示例代码 2:

var foo = 33;
{
  let foo = (foo + 55); // ReferenceError: foo is not defined
}


注:首先,需要分清变量的创建、初始化、赋值是三个不同的过程。另外,从 ES5 开始用词法环境(Lexical Environment)替代了 ES3 中的变量对象(Variable object)来管理静态作用域,但作用是相同的。为了方便理解,上述讲解中仍保留使用变量对象来进行描述。


小结

  1. var 声明的变量在执行上下文创建阶段就会被「创建」和「初始化」,因此对于执行阶段来说,可以在声明之前使用。
  2. let 声明的变量在执行上下文创建阶段只会被「创建」而不会被「初始化」,因此对于执行阶段来说,如果在其定义执行前使用,相当于使用了未被初始化的变量,会报错。


3.let 与 const 异同

const 与 let 很类似,都具有上面提到的 let 的特性,唯一区别就在于 const 声明的是一个只读变量,声明之后不允许改变其值。因此,const 一旦声明必须初始化,否则会报错。

示例代码:

let a;
const b = "constant"

a = "variable"
b = 'change' // TypeError: Assignment to constant variable


如何理解声明之后不允许改变其值?

其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动(即栈内存在的值和地址)。

JavaScript 的数据类型分为两类:原始值类型和对象(Object类型)。

对于原始值类型(undefined、null、true/false、number、string),值就保存在变量指向的那个内存地址(在栈中),因此 const 声明的原始值类型变量等同于常量。

对于对象类型(object,array,function等),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是不可修改的,至于指针指向的数据结构是无法保证其不能被修改的(在堆中)。

示例代码:

const obj = {
  value: 1
}

obj.value = 2

console.log(obj) // { value: 2 }

obj = {} // TypeError: Assignment to constant variable

相关推荐

一个简单便捷搭建个人知识库的开源项目(MDwiki)

这里我通过自动翻译软件,搬运总结MDwiki官网的部署和使用方法。第一步:下载编译好的后MDwiki文件,只有一个HTML文件“mdwiki.html”。第二步:在mdwiki.html同级目录创建“...

强大、简洁、快速、持续更新 PandaWiki新一代 AI 驱动的开源知识库

PandaWiki是什么PandaWiki是一款AI大模型驱动的开源知识库搭建系统,帮助你快速构建智能化的产品文档、技术文档、FAQ、博客系统,借助大模型的力量为你提供AI创作、AI问答...

DeepWiki-Open: 开源版Deepwiki,可自己构建github文档库

Deepwiki是Devin团队开发的github文档库,用户能免费使用,但代码不是开源,而DeepWiki-Open侧是开源版本的实现。DeepWiki-Open旨在为GitHub和GitLa...

最近爆火的wiki知识管理开源项目PandaWiki

项目介绍PandaWiki是一款AI大模型驱动的开源知识库搭建系统,帮助你快速构建智能化的产品文档、技术文档、FAQ、博客系统,借助大模型的力量为你提供AI创作、AI问答、AI搜索等...

轻量级开源wiki系统介绍(轻量开源论坛系统)

wiki系统有很多DokuWiki、MediaWiki、MinDoc等等都是开源的wiki系统。商业版的wiki,像很多企业在用的confluence等。今天我们讲的是一款轻量级且开源的文档管理系统:...

DNS解析错误要怎么处理(dns解析状态异常怎么办)

在互联网时代,网络已经成为人们生活和工作中不可或缺的一部分。然而,当遇到DNS解析错误时,原本畅通无阻的网络访问会突然陷入困境,让人感到十分困扰。DNS,即域名系统,它如同互联网的电话簿,将人们易于...

网页加载慢?这些方法让你秒开网页!

打开浏览器,信心满满地准备查资料、看视频或者追剧,却发现网页怎么都打不开!是不是瞬间感觉手足无措?别慌,这个问题其实挺常见,而且解决起来并没有你想象的那么复杂。今天就来聊聊网页打不开究竟是怎么回事,以...

windows11 常用CMD命令大全(windows11msdn)

Windows11中的命令提示符(CMD)是一个强大的工具,可以通过命令行执行各种系统操作和管理任务。以下是一些常用的CMD命令,按功能分类整理,供你参考:一、系统信息与状态systeminfo显...

电脑提示DNS服务器未响应怎么解决?

我们在使用电脑的时候经常会遇到各种各样的网络问题,例如最近就有Win11电脑用户在使用的时候遇到了DNS未响应的问题,遇到这种情况我们应该怎么解决呢?  方法一:刷新DNS缓存  1、打开运行(W...

宽带拨号错误 651 全解析:故障定位与修复方案

在使用PPPoE拨号连接互联网时,错误651提示「调制解调器或其他连接设备报告错误」,通常表明从用户终端到运营商机房的链路中存在异常。以下从硬件、系统、网络三层维度展开排查:一、故障成因分类图...

如何正确清除 DNS 缓存吗?(解决你访问延时 )

DNS缓存是一个临时数据库,用于存储有关以前的DNS查找的信息。换句话说,每当你访问网站时,你的操作系统和网络浏览器都会保留该域和相应IP地址的记录。这消除了对远程DNS服务器重复查询的...

网络配置命令:ipconfig和ifconfig,两者有啥区别?

在计算机网络的世界里,网络接口就像是连接你电脑和外部网络的桥梁,而网络配置则是确保这座桥梁稳固、通信顺畅的关键。提到网络配置工具,ipconfig和ifconfig绝对是两个绕不开的名字。它们一...

救急的命令 你会几个?(救急一下)

很多人都说小编是注册表狂魔,其实不完全是,小编常用的命令行才是重点。其实所谓的命令行都是当初DOS时代的标准操作方式,随着Windows不断演化,DOS的命令早已成为Windows的一部分了——开始菜...

电脑有网却访问不了GitHub原来是这样

当满心欢喜打开电脑,准备在GitHub这个“开源宝藏库”里挖掘点超酷的项目,却遭遇了网页无法访问的尴尬。看着屏幕上那令人无奈的提示,原本高涨的热情瞬间被泼了一盆冷水,是不是感觉世界都不美好了...

rockstargames更新慢| r星更新速度 怎么办 解决办法

rockstargames更新慢|r星更新速度怎么办解决办法说到RockstarGames,那可是游戏界的大佬,作品个顶个的经典。但话说回来,每当新内容更新时,那蜗牛般的下载速度,真是让人急得...

取消回复欢迎 发表评论: