导言:ES6引入了const let 从而引入了块作用域的概念,使JS的内容更为丰富,那么var const let这三者的区别是什么呢?
区别
constlet具有块状作用域的作用,var没有var具有变量提升的特点,constlet不具有- 在声明语句之前访问
constlet所声明的变量会报错,这也是第二点的补充
块状作用域
在es6之前只有函数作用域和全局作用域,除开这两个作用域,在{}内部用var声明的变量是在外部是可以正常访问的,es6之后出现了块状作用域,即引入了let const,使得{}也有了作用域的功能,即在{}内定义const let,外部无法访问let const所定义的变量,如下代码:
// var
function testVar(){
if(1) {
var b = 3;
}
console.log(b);
}
testVar(); // 3
// let
function testLet(){
if(1) {
let b = 3;
}
console.log(b);
}
testLet(); // error: Uncaught ReferenceError: b is not defined
变量提升
在作用域中,用var所声明变量,是可以提前访问的,只不过该值为undefined,而const let则会报错;
// var
function testVar(){
console.log(b); // undefined
var b = 3;
}
testVar();
// let
function testLet(){
console.log(b); // Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 3;
}
testLet();
简单的说,其实就是对于var的声明而言,会做类似以下的操作:
// 模拟var的变量提升
function testVar(){
var b = undefined;
console.log(b);
b = 3;
}
testVar();
所以它正常输出了
那都是对变量进行声明的操作,为什么两者有如此区别呢?(const let算是同一者,因为它们之间只是一个声明变量,一个声明常量的区别而已)
JS代码的执行过程
造成两者如此差别的原因,就得从一段JS代码的执行过程开始说起!
// 一段JS代码
test();
console.log(testVar);
function test(){
console.log('test function');
}
var testVar = 'testVar';
上述一段代码,在JavaScript引擎是怎么执行的呢?
其实JavaScript引擎执行一段代码的过程分两个阶段
- 编译阶段
- 执行阶段
编译阶段
在编译阶段过程中,代码会分为两部分
- 执行上下文
- 可执行代码
在执行上下文中,分为两个部分一个部分为
变量环境,另外一个部分为词法环境
变量环境里面放的就是变量声明以及函数声明,其实这就是变量提升的实际操作,引擎为test函数以及testVar开辟出内存,内存里面的初始化,如testVar为undefined而函数声明则直接是函数体,这也是为什么test函数以及testVar在前面能正常执行;
// 模拟编译完之后的代码的执行顺序,模拟!!实际并不是这样
function test(){
console.log('test function');
}
var testVar = undefined;
test();
console.log(testVar);
testVar = 'testVar';
执行阶段
而执行阶段就要简单得多:
- test函数入栈执行
- testVar赋值为
'testVar'
那么来看看const let的块状作用域是怎么编译的?
// 一段含有let,const的JS代码
var a = 1;
var b = 2;
let c = 3;
{
var d = 4;
let e = 5;
var f = 6;
}
执行过程:
之前编译阶段的词法环境终于用上场了
在这里,var声明的变量,不管是在{}外面还是里面,都会放在变量环境里面做变量提升的操作,而let以及const声明的变量,则会进入到词法环境中类似一个栈结构一样,被维护起来
具体流程如下:
a,b,d,f进入变量环境做变量提升{}外的d先入词法环境的栈{}内的e后入词法环境的栈至此,完成编译阶段
那执行阶段是如何访问这些变量的呢
先从词法环境的栈顶开始访问,一直到栈底,最终返回到变量环境进行访问
如: a = 1赋值语句先从词法环境的栈顶开始访问,一直到栈底,最终返回到变量环境进行访问,最终给a赋值为1,而c = 3则直接从词法环境访问到了,就不进入变量环境了
当然,词法环境中的变量是被{}限定的,每一次入栈对应的是一个作用域,如c则是全局的作用域,所以只能在全局上下文中被访问到,而e则是在{}内的作用域,只能在{}中被访问到,{}中的代码执行完后,e的词法作用域出栈,变量被销毁,就如同函数的出入栈一致
总结
const let 与var的区别主要是:
constlet具有块状作用域的作用,var没有var具有变量提升的特点,constlet不具有- 在编译阶段,
var放入到变量环境中,而constvar放入到词法环境中,并维护一个类似栈的词法作用域栈
本文内容为自己对var const let三者区别的总结与归纳,如有问题,请指正!