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

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 函数的作用域链,其结果如图:

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)组成一个变量对象的属性被创建(创建并初始化)

如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyssd.html