关于let/const不会造成变量提升的误解与纠正
在JavaScript中,肯定不可避免的需要声明变量和函数,但是,有这么一种现象:
即使我们在定义这个函数之前调用它,函数仍然可以工作。
即使我们在定义这个变量之前调用它,代码也不会报错。
初学的时候,上网查资料,得到的结论是
变量提升
,对于当时的我来说,变量声明提前
这个说法更好理解。对此,MDN给出的解释是这是因为在 JavaScript 中执行上下文的工作方式造成的
。,那我们先简单聊聊
执行上下文
执行上下文(Execution Context)
执行上下文:英文直译,理解起来有点奇怪,可以简单理解成
当前代码的执行环境
。每次当控制器转到可执行代码的时候,就会进入一个执行
执行上下文
。它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。- 全局环境:JavaScript代码运行起来会首先进入该环境;
- 函数环境:当函数被调用执行时,会进入当前函数中执行代码;
更多请见执行上下文详解
执行上下文的生命周期
执行上下文的生命周期分为三个阶段:
- 创建阶段(函数被调用时触发)在这个阶段中,执行上下文会分别
创建变量对象
,确定this指向
,以及其他需要的状态;
- 代码执行阶段创建完成后,就会开始执行代码,会完成
变量赋值
,以及执行其他代码;
- 销毁阶段可执行代码执行完毕之后,执行上下文
出栈
,对应的内存空间失去引用
,等待被回收;
其中,创建变量对象与变量提升密切相关
变量对象
{
a: 10
}
变量对象的创建,依次经历以下几个过程:
一:建立arguments对象:检查当前上下文中参数,建立该对象下的属性与属性值;
二:检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值指向
该地址所在内存
的地址的引用
;三:检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量建立一个属性,属性值为
undefined
,const/let声明的变量没有赋值,不能提前使用;* const/let声明的变量不能使用是因为没有赋值,不是因为没有声明;(误解)
* 如果var变量与函数同名,则在这个阶段(执行上下文创建阶段),以函数值为准;在下一个阶段(执行上下文执行阶段),函数值会被变量值覆盖;(难点)
简单示例:
console.log(foo);
// function foo
function foo() { console.log('function foo') }
var foo = 20;
// 上述的执行顺序为
// 首先将所有函数声明放入变量对象中
function foo() { console.log('function foo') }// 其次将所有变量声明放入变量对象中,但是因为foo已经存在同名函数,此时以函数值为准,而不会被undefined覆盖
// var foo = undefined;
// 然后开始执行阶段代码的执行
console.log(foo);
// function foo
foo = 20;
文章图片
上述规则可以看出,function声明会比var声明优先级更高一点。
// demo2
function test() {
console.log(foo);
console.log(bar);
var foo = 'Hello';
console.log(foo);
var bar = function () {
return 'world';
}function foo() {
return 'hello';
}
}test();
上例全局作用域中运行test()时,test()的执行上下文开始创建。为了便于理解,我们用如下形式表示:
// 创建过程
testEC = {
// 变量对象
VO: {},
scopeChain: {}
}// 因为本文暂时不详细解释作用域链,所以把变量对象专门提出来说明// VO 为 Variable Object的缩写,即变量对象
VO = {
arguments: {...},//注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理
foo: // 表示foo的地址引用;函数声明比var声明优先级高
bar: undefined
}
未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变成活动对象,里面的属性便能访问了,之后开始执行阶段的操作。
tips:变量对象和活动对象其实是同一个对象,只是执行上下文不同的生命周期;变量对象处于创建阶段,活动对象处于执行阶段;
// 执行阶段
VO ->AO// Active Object
AO = {
arguments: {...},
foo: 'Hello',// 到执行阶段,foo的值被覆盖
bar: ,
this: Window
}
let/const
let/const作为ES6新增关键字,可以形成块作用域。但是有一个问题,let/const 声明的变量,是否还会变量提升?
【关于let/const不会造成变量提升的误解与纠正】实验验证:
第一步:我们直接使用一个未定义的变量
console.log(a);
报错信息显示
未定义
:文章图片
第二步:在let定义之前使用变量
console.log(a);
let a = 10;
报错信息显示
变量定义了,但是没有初始化
:文章图片
结论:
let/const
声明的变量,仍然会提前被收集到变量对象中
,但和var
不同的是,let/const定义的变量,不会再这个时候给它赋值undefined。因为没有赋值,所以即使变量声明提前了,我们也不能再赋值之前调用他,这就是我们常说的暂时性死区。
参考资料
1.执行上下文详解
2.MDN变量提升
3.变量对象详解
推荐阅读
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- 四首关于旅行记忆的外文歌曲
- (二)ES6第一节变量(let|(二)ES6第一节变量(let,const)
- 醒不来的梦
- 关于自我为中心的一点感想
- 「按键精灵安卓版」关于全分辨率脚本的一些理解(非游戏app)
- 关于Ruby的杂想
- 关于读书的思考
- 关于this的一些问题(1)
- 《声之形》