深入理解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 函数的作用域链,其效果如图:

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

由上图可知,let 在全局环境声明变量 bar 保留在[[Scopes]][0]: Script这个变量工具的属性中,而[[Scopes]][1]: Global就是我们常说的全局工具。

(4)变量提升与暂存死区

var 声明变量存在变量提升,若何明白变量提升呢?

要注释清晰这个,就要涉及到执行上下文变量工具

在 JavaScript 代码运行时,注释执行全局代码、挪用函数或使用 eval 函数执行一个字符串表达式都市建立并进入一个新的执行环境,而这个执行环境被称之为执行上下文。因此执行上下文有三类:全局执行上下文、函数执行上下文、eval 函数执行上下文。

执行上下文可以明白为一个抽象的工具,如下图:

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

Variable object:变量工具,用于存储被界说在执行上下文中的变量 (variables) 和函数声明 (function declarations) 。

Scope chain:作用域链,是一个工具列表 (list of objects) ,用以检索上下文代码中泛起的标识符 (identifiers) 。

thisValue:this 指针,是一个与执行上下文相关的特殊工具,也被称之为上下文工具。

一个执行上下文的生命周期可以分为三个阶段:建立、执行、释放。如下图:

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

而所有使用 var 声明的变量都市在执行上下文的建立阶段时作为变量工具的属性被建立并初始化,这样才气保证在执行阶段能通过标识符在变量工具里找到对应变量举行赋值操作等。

而用 var 声明的变量构建变量工具时举行的操作如下:

  • 由名称和对应值(undefined)组成一个变量工具的属性被建立(建立并初始化)
  • 若是变量名称跟已经声明的形式参数或函数相同,则变量声明不会滋扰已经存在的这类属性。

上述历程就是我们所谓的“变量提升”,这也就能注释为什么变量可以在声明之前使用,由于使用是在执行阶段,而在此之前的建立阶段就已经将声明的变量添加到了变量工具中,以是执行阶段通过标识符可以在变量工具中查找到,也就不会报错。

示例代码:

console.log(a) // undefined

var a = 1;

console.log(a) // 1

let 声明变量存在暂存死区,若何明白暂存死区呢?

实在 let 也存在与 var 类似的“变量提升”历程,但与 var 差别的是其在执行上下文的建立阶段,只会建立变量而不会被初始化(undefined),而且 ES6 划定了其初始化历程是在执行上下文的执行阶段(即直到它们的界说被执行时才初始化),使用未被初始化的变量将会报错。

let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

在变量初始化前接见该变量会导致 ReferenceError,因此从进入作用域建立变量,到变量最先可被接见的一段时间(历程),就称为暂存死区(Temporal Dead Zone)。

示例代码 1:

console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined

var bar = 1;
let foo = 2;

示例代码 2:

var foo = 33;
{
  let foo = (foo + 55); // ReferenceError: foo is not defined
}

注:首先,需要分清变量的建立、初始化、赋值是三个差别的历程。另外,从 ES5 最先用词法环境(Lexical Environment)替换了 ES3 中的变量工具(Variable object)来治理静态作用域,但作用是相同的。为了利便明白,上述解说中仍保留使用变量工具来举行形貌。

小结

  1. var 声明的变量在执行上下文建立阶段就会被「建立」和「初始化」,因此对于执行阶段来说,可以在声明之前使用。

  2. let 声明的变量在执行上下文建立阶段只会被「建立」而不会被「初始化」,因此对于执行阶段来说,若是在其界说执行前使用,相当于使用了未被初始化的变量,会报错。

3.let 与 const 异同

const 与 let 很类似,都具有上面提到的 let 的特征,唯一区别就在于 const 声明的是一个只读变量,声明之后不允许改变其值。因此,const 一旦声明必须初始化,否则会报错。

示例代码:

let a;
const b = "constant"

a = "variable"
b = 'change' // TypeError: Assignment to constant variable

若何明白声明之后不允许改变其值?

实在 const 实在保证的不是变量的值稳定,而是保证变量指向的内存地址所保留的数据不允许改动(即栈内存在的值和地址)。

JavaScript 的数据类型分为两类:原始值类型和工具(Object类型)。

对于原始值类型(undefined、null、true/false、number、string),值就保留在变量指向的谁人内存地址(在栈中),因此 const 声明的原始值类型变量等同于常量。

对于工具类型(object,array,function等),变量指向的内存地址实在是保留了一个指向现实数据的指针,以是 const 只能保证指针是不能修改的,至于指针指向的数据结构是无法保证其不能被修改的(在堆中)。

示例代码:

const obj = {
  value: 1
}

obj.value = 2

console.log(obj) // { value: 2 }

obj = {} // TypeError: Assignment to constant variable

4.参考

var – JavaScript | MDN

let – JavaScript – MDN – Mozilla

const – JavaScript – MDN – Mozilla

深入明白JavaScript系列(12):变量工具(Variable Object)

ES6 let 与 const

详解ES6暂存死区TDZ

嗨,你知道 let 和 const 吗?

原创文章,作者:28x29新闻网,如若转载,请注明出处:https://www.28x29.com/archives/13208.html