第 83 题:var、let 和 const 区别的实现原理是什么?
1、作用域不同
var 是函数作用域,let 是块作用域。
在函数中声明了 var,整个函数内都是有效的,比如说在 for 循环内定义的一个 var 变量,实际上其在 for 循环以外也是可以访问的。
而 let 由于是块作用域,所以如果在块作用域内定义的变量,比如说在 for 循环内,在其外面是不可被访问的,所以 for 循环推荐用 let。
2、let 不能在定义之前访问该变量,但是 var 可以
let 必须先声明,在使用。而 var 先使用后声明也行,只不过直接使用但没有定义的时候,其值是 undefined。var 有一个变量提升的过程,当整个函数作用域被创建的时候,实际上var定义的变量都会被创建,并且如果此时没有初始化的话,则默认为初始化一个 undefined。
3、let 不能被重新定义,但是 var 是可以的
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(26)
下面先让我们看看它和 var 之间用法的不同
上面在代码块中声明了两个变量并分别赋值输出到控制台,结果 a 的变量成功输出,b 的输出结果产生了报错。可见,let 声明的变量只在它所在的代码块中产生作用。同时,我们也能想到它最好的使用方法就是在 for 循环中使用。
在词法环境内部,维护了一个小型栈结构,栈底是函数最外层的变量,进入一个作用域块后,就会把该作用域块内部的变量压到栈顶;当作用域执行完成之后,该作用域的信息就会从栈顶弹出
进入块级作用域不会有编译过程,只不过通过let或者const声明的变量会在进入块级作用域的时被创建,但是在该变量没有赋值之前,引用该变量JavaScript引擎会抛出错误---这就是“暂时性死区”。
const 实现
function _const(key, value) {
const desc = {
value,
writable: false
}
Object.defineProperty(window, key, desc)
}
var let const 都存在变量提升,而且都初始化为undefined,这个可以在浏览器控制台debugger Scope的Local中看到三者都是undefined。
js执行的时候会先编译 对于该作用域下的var变量会初始化为undefined ,var声明的变量存在于变量活动对象中,let const声明的变量也会初始化为undefined,但是是在词法环境中,而js引擎对词法环境中未初始化的let const声明的变量做了访问限制 因此才会存在“暂时性死区”。
这块内容标准中有写https://262.ecma-international.org/6.0/
13.3.1和13.3.2
生命周期
https://rain120.github.io/study-notes/fe/javascript/key-concept/var-let-const-function-lifecycle
var,已废弃
let/const,更现代
var 存在变量提升, let const 声明的变量不会
我的理解:
代码执行分为两个阶段,create phase 和 execute phase,也就是代码解析和代码执行;
在create phase 阶段,其实var 和 let 定义的变量都已经初始化了:
var定义的变量被赋值为undefined
let定义的变量被赋值为uninitialized
var之所以被变量提升是因为已经赋有效值(undefined),但是引用uninitialized变量编译器会报错。
等到execute phase阶段,a赋值为1,b赋值为2,就没什么可说的了。
另外块级作用域也是因为通过不同方式声明的变量被赋值到不同的EnvironmentRecord,LexicalEnvironment和VariableEnvironment的区别,在一个方法内部,每一个块级代码都会新生成一个LexicalEnvironment,并保留上一个LexicalEnvironment的引用,但是一个方法内只有一个VariableEnvironment。
ECMAScript 规范是如何告诉我们let, const, var 的区别的
并且可能非常多前端开发者对于计算基础的知识并不牢靠。比如什么是栈和堆?操作系统的栈和堆概念和编程语言的栈、堆概念一致吗(一般我们了解程序的堆栈概念来自于 C 语言)?和传统的数据结构中的栈结构和二叉堆又有什么关联呢?等等这些概念。
了解了这些概念,是不是有必要去研究下 GC(garbage collection)?
前端可能更加偏向于体验一点。
明白了 十分感谢解惑 因为总觉得这样回答似乎很简单 看来是我想复杂了
其实题目想要考察的就是 javascript 表面语义的东西。
具体不同类型的声明产生的变量在内存中究竟如何存储,那要看 javascript engine 内部是如何实现的。这些实现细节对于上层前端开发者来讲了解即可。如果你真的想要了解究竟是分配在栈上还是堆上,去看 V8,spiderMonkey这些引擎的代码(个人觉得如果不做引擎这块,纯属看个人兴趣去深入了)。这些引擎内部是用 C 或 C++ 这种更加方面操作内存的语言写的。也就知道实现原理是什么了。
怎么感觉大家回答的都是表层的东西...栈和堆这些都知道哪个可改变哪个不可改变...问的应该是怎么做到不可改变的吧?
转载:
var和let的区别,面试老生常谈的问题,大多数人回答可能就是作用域和变量提升这两点不同,少有人能够知道内在原理,这样的回答面试官会满意吗?(手动滑稽)
我们就从声明过程,内存分配,和变量提升这三点来看这三者之间的区别。
一.声明过程
var:遇到有var的作用域,在任何语句执行前都已经完成了声明和初始化,也就是变量提升而且拿到undefined的原因由来
function: 声明、初始化、赋值一开始就全部完成,所以函数的变量提升优先级更高
let:解析器进入一个块级作用域,发现let关键字,变量只是先完成声明,并没有到初始化那一步。此时如果在此作用域提前访问,则报错xx is not defined,这就是暂时性死区的由来。等到解析到有let那一行的时候,才会进入初始化阶段。如果let的那一行是赋值操作,则初始化和赋值同时进行
const、class都是同let一样的道理
比如解析如下代码步骤:
{
// 没用的第一行
// 没用的第二行
console.log(a) // 如果此时访问a报错 a is not defined
let a = 1
}
步骤:
发现作用域有let a,先注册个a,仅仅注册
没用的第一行
没用的第二行
a is not defined,暂时性死区的表现
假设前面那行不报错,a初始化为undefined
a赋值为1
对比于var,let、const只是解耦了声明和初始化的过程,var是在任何语句执行前都已经完成了声明和初始化,let、const仅仅是在任何语句执行前只完成了声明。
二.内存分配
var,会直接在栈内存里预分配内存空间,然后等到实际语句执行的时候,再存储对应的变量,如果传的是引用类型,那么会在堆内存里开辟一个内存空间存储实际内容,栈内存会存储一个指向堆内存的指针
let,是不会在栈内存里预分配内存空间,而且在栈内存分配变量时,做一个检查,如果已经有相同变量名存在就会报错
const,也不会预分配内存空间,在栈内存分配变量时也会做同样的检查。不过const存储的变量是不可修改的,对于基本类型来说你无法修改定义的值,对于引用类型来说你无法修改栈内存里分配的指针,但是你可以修改指针指向的对象里面的属性
三.变量提升
let const 和var三者其实会存在变量提升
let只是创建过程提升,初始化过程并没有提升,所以会产生暂时性死区。
var的创建和初始化过程都提升了,所以在赋值前访问会得到undefined
function 的创建、初始化、赋值都被提升了
————————————————
版权声明:本文为CSDN博主「微 光」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Web_J/article/details/99591116
咋回事咧?这么明显的错误~
let const 和var三者都会存在变量提升
var的话会直接在栈内存里预分配内存空间,然后等到实际语句执行的时候,再存储对应的变量,如果传的是引用类型,那么会在堆内存里开辟一个内存空间存储实际内容,栈内存会存储一个指向堆内存的指针
let的话,是不会在栈内存里预分配内存空间,而且在栈内存分配变量时,做一个检查,如果已经有相同变量名存在就会报错
const的话,也不会预分配内存空间,在栈内存分配变量时也会做同样的检查。不过const存储的变量是不可修改的,对于基本类型来说你无法修改定义的值,对于引用类型来说你无法修改栈内存里分配的指针,但是你可以修改指针指向的对象里面的属性
变量被重新覆盖之后,之前的内存地址和内存空间,会被垃圾回收机制回收的吧!
搬运:
比如解析如下代码步骤:
步骤:
对比于var,let、const只是解耦了声明和初始化的过程,var是在任何语句执行前都已经完成了声明和初始化,let、const仅仅是在任何语句执行前只完成了声明
针对区别做了总结,实现原理属于推测,求真相。
2019.05.29面试题: var、let、const 的区别及实现原理?
一、实现原理
(一)、var的实现原理
(二)、let的实现原理
(三)、const的实现原理
const
声明一个只读的常量。一旦声明,常量的值就不能改变。二、应用场景
(一)var应用场景
(二)let应用场景
for
循环的计数器,就很合适使用let
命令(三)const应用场景
三、var、let、const的区别
(一)var
var
命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined
。(二)let
ReferenceError
let
命令,它所声明的变量就“绑定”(binding
)这个区域,不再受外部的影响,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。(三)const
const
声明之后必须马上赋值,否则会报错const
简单类型一旦声明就不能再更改,�复杂类型(数组、对象等)指针指向的地址不能更改,内部数据可以更改。const
一旦声明变量,就必须立即初始化,不能留到以后赋值。const
命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。四、参考文章
var 与 let,const的区别
先说说这三者的区别吧:
var 和 let 用以声明变量,const 用于声明只读的常量;
var 声明的变量,不存在块级作用域,在全局范围内都有效,let 和 const 声明的,只在它所在的代码块内有效;
let 和 const 不存在像 var 那样的 “变量提升” 现象,所以 var 定义变量可以先使用,后声明,而 let 和 const 只可先声明,后使用;
let 声明的变量存在暂时性死区,即只要块级作用域中存在 let,那么它所声明的变量就绑定了这个区域,不再受外部的影响。
let 不允许在相同作用域内,重复声明同一个变量;
const 在声明时必须初始化赋值,一旦声明,其声明的值就不允许改变,更不允许重复声明;
然后是原理,原理确实没认真研究过,在网上翻了一番资料,结合自己的理解简单说下(纯属个人愚见,高手轻喷):
变量与内存之间的关系,主要由三个部分组成:
JS 引擎在读取变量时,先找到变量绑定的内存地址,然后找到地址所指向的内存空间,最后读取其中的内容。当变量改变时,JS 引擎不会用新值覆盖之前旧值的内存空间(虽然从写代码的角度来看,确实像是被覆盖掉了),而是重新分配一个新的内存空间来存储新值,并将新的内存地址与变量进行绑定,JS 引擎会在合适的时机进行 GC,回收旧的内存空间。
const 定义变量(常量)后,变量名与内存地址之间建立了一种不可变的绑定关系,阻隔变量地址被改变,当 const 定义的变量进行重新赋值时,根据前面的论述,JS 引擎会尝试重新分配新的内存空间,所以会被拒绝,便会抛出异常。
坐等高手答疑。