面试的时候,咱们常常会被问到let 、const 和 var 之间的区别,个人回答无疑老是那几点:面试
变量提高bash
var会进行变量提高,let和const不会进行提高函数
暂存死区学习
由于var会进行变量提高,因此能够在声明以前访问,不会造成暂存死区。ui
let 和const 不会进行变量提高,在声明以前不能使用,造成暂存死区spa
重复声明code
var能够进行重复声明,可是let和const不能进行重复声明cdn
块做用域对象
var不会造成块做用域,let和const能够造成块做用域blog
从新赋值
var和let声明的变量能够从新赋值,const不能够。
若是const 声明的变量存储的是引用地址, 是能够修改这个引用对应的对象的值的,可是这个变量不能被赋予其余值
每次都是这个样回答,面试官也没有说什么。
但有一次面试的时候,一面面试官问到我,let的所谓的暂时性死区怎么解释?let和const到底有没有变量提高?我没回答出来,而后面试官和我解释了一下,而且推荐我看一个大神的这篇文章:zhuanlan.zhihu.com/p/28140450
经过这篇文章,以及这篇文章所提到的文章得出的结论就是:
接下来就讲为何?从变量的生命周期开始!
在这里我比较喜欢这篇文章:JavaScript Variables Lifecycle: Why let Is Not Hoisted
首先咱们来学习一下变量的生命周期:
当引擎使用变量时,它们的生命周期包括如下几个阶段:
Declaration phase
)正在范围内注册变量。Initialization phase
)是分配内存并为做用域中的变量建立绑定。在此步骤中,变量将使用进行自动初始化undefined。Assignment phase
)是为初始化变量分配一个值。变量在经过声明阶段时已处于统一状态,但还没有达到初始化状态。
可是要注意的是,就变量生命周期而言,声明阶段与通常而言的变量声明是不一样的术语。简而言之,引擎在三个阶段处理变量声明:声明阶段,初始化阶段和赋值阶段。
var声明的变量在所在的做用域的开始处声明和初始化变量。声明和初始化阶段之间没有差距。
function var_variable(){
console.log('在声明阶段以前',one_variable);
var one_variable;
console.log('在未赋值以前',one_variable);
one_variable = 'be assigned';
console.log('赋值以后', one_variable);
}
//console.log('在函数做用域外是否能够访问到var声明的变量', one_variable);//one_variable is not defined
var_variable();
复制代码
运行结果:
在未声明阶段以前 undefined
在未赋值以前 undefined
赋值以后 be assigned
复制代码
在执行任何语句以前,变量在做用域的开头经过声明阶段并当即初始化阶段(上图步骤1)。 var variable语句在函数做用域中的位置不影响声明和初始化阶段。
在声明和初始化以后,可是在赋值阶段以前,该变量具备undefined值而且能够被使用。
在赋值阶段 variable = 'value',变量将接收其初始值(上图步骤2)。
函数声明:function funName() {...}
function 声明会在代码执行以前就「建立、初始化并赋值」。
function sumArray(array) {
return array.reduce(sum);
function sum(a, b) {
return a + b;
}
}
sumArray([5, 10, 8]); // => 23
复制代码
执行JavaScript时sumArray([5, 10, 8]),它将进入sumArray函数范围。在此范围内,紧接着执行任何语句以前,sum将经过全部三个阶段:声明,初始化和赋值。 这种方式甚至array.reduce(sum)能够sum在其声明语句以前使用function sum(a, b) {...}。
let变量的处理方式不一样于var。主要区别是声明和初始化阶段是分开的。
let a = 1;
{
// 初始化前没法访问“a”,在这里的a指的是下面的a,不是全局的a,此时a尚未被初始化,
//因此在这里log会报错,由于在这里是暂时性死区
// console.log(a);
let a;
//解释器进入包含let variable语句的块范围的状况。变量当即经过声明阶段,在范围内注册其名称,
//而后解释器继续逐行解析块语句。
console.log('########## - a', a);//初始化,对其进行访问的结果为undefined
a='被从新赋值了'
console.log('========= - a', a);
console.log('########## - b', b);
var b;
b='b';
console.log('========= - b', b);
}
console.log('全局的a',a);
console.log('全局的b', b);
复制代码
运行结果:
########## - a undefined
========= - a 被从新赋值了
########## - b undefined
========= - b b
全局的a 1
全局的b b
复制代码
我认为:在块级做用域中,从块级做用域中的第一行开始,到用let variable
声明变量这一行以前,这一段区域是let的暂时性死区。
这个暂时性死区就是在上图的Uninitialized state
阶段,若是在这一阶段的时候,访问这个变量就会报错。
在初始化阶段Initialized state
以后就能够访问这个let
变量了。
Hosting
在生命周期中无效如上所述,提高是变量在顶部的耦合声明和初始化。 可是let
声明的变量,他的生命周期使声明和初始化阶段脱钩。解耦消除了 Hosting
的术语let
。 这两个阶段之间的间隙建立了临时死区,没法访问该变量。
let
的「建立」过程被提高了,可是初始化没有提高。由于声明和初始化阶段是分离的,因此提高对于let
变量(包括for const
和class
)无效。在初始化以前,该变量位于时间死区中而且不可访问。
在这里再提一下:
若是
let x
的初始化过程失败了,那么x
变量就将永远处声明(Declaration
)状态。 你没法再次对x
进行初始化(初始化只有一次机会,而那次机会你失败了)。 因为 x 没法被初始化,因此x
永远处在暂时死区.