ES6中的let和const命令

121 阅读3分钟

ECMAScript6 中的let和const

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言,如果你学习完了JavaScript的全部内容,那接下来你需要学习的肯定是ES6,在这其中有许多新型的语法和新增的扩展方法,接下来让我们来学习ES6的第一个知识点let和const命令

ES6当中新增了let和const命令来声明变量 接下来让我来康康这新增的两个命令有什么作用

let

以前我们声明变量的方式都是使用var

//这是用var声明变量
var a = 10
//这是用let声明变量
let b = 10

废话少说让我们直接看看它们有什么不同的地方

{
    var a = 3
    let b = 4
}
//在代码块之外使用变量
console.log(a) //3
console.log(b) //ReferenceError: a is not defined.

let声明的变量只在它所在的代码块中有效,在这里大括号代表的是一个区块 也是ES6新增的一个作用域 也就是说如果我们使用let声明变量在if语句的{}中或者for循环语句的{}中,变量就会存在于块级作用域中

让我们来看个例子:由于i是在for循环代码块中定义的 所以在外层console会报错 也就是说let是不存在变量提升的

for(let i = 0; i < 10; i++){
    //某些代码
}
console.log(i) // ReferenceError: i is not defined

再看一个例子:这个例子可以说是let很经典的一个例子 刚开始学习let可能要理解很久这个例子

var a = []  //定义a是一个数组
for(var i = 0; i < 10; i++){
    a[i] = function(){
     console.log(i) //这个循环原本想达到的目的是取a数组的第n位里的console.logi
  }
}
a[6]() // 10  结果就是无论在[]中填多少结果都是10

其实不难理解这个例子 因为var定义变量是存在提升的 当循环开始的时候var i已经被提升到了全局,然后for循环内部是一个函数表达式,需要执行函数才会打印结果,当我们最后一步执行函数的时候 for循环早已完成 变量i也早已+到了10 所以最后打印结果i是10

如果是用let就不会存在这个问题
var a = []
for(let i = 0; i < 10; i++){
    a[i] = function(){
      console.log(i)
  }
}
a[6]() // 6

因为i是let声明的 所以该变量只在本轮循环有效 所以每次循环的i都是一个新的变量 js引擎内部会记住上一轮循环的值(每次循环i的值供后面执行函数时把本次循环的i赋给函数体的变量i),初始化本轮的变量i时(最后一步执行函数),就在上一轮循环的基础上进行计算。

这里还有一个for循环特殊的例子
for(let i = 0; i < 10; i++){
    let i = 'abc';
    console.log(i) 
}
//abc
//abc
//abc
//打印了三个abc,可以看出小括号里定义的i和大括号里定义的i不在同一作用域,它们有各自独立的作用域

let不存在变量提升 也就是说不能在定义前使用变量

console.log(a);
var a = 123
// undefined
//用var定义的变量会提升到代码头部,在定义前使用会是undefined表示变量已定义但是还没赋值
---------------
console.log(a);
let a = 123
//报错
//而用let定义的变量不存在变量提升,在定义前使用会报错

暂时性死区 TDZ

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

只要代码块内有let定义变量 那么该变量不会被外部影响 上面例子中虽然外层有一个变量tmp 然后内部先对tmp进行了赋值 但是下面又用let定义了一个tmp 所以tmp变量被绑定在了该区块中 所以会报错。

ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

暂时性死区也意味着typeof不是百分百不报错的操作了

typeof x; // ReferenceError
let x;
---------------------------
// 不报错
var x = x;
// 报错
let x = x;
//因为存在TDZ把x赋值给x 这个时候的x还是没有定义的状态 所以报错
// ReferenceError: x is not defined

不允许重复声明

let不允许在相同的作用域内声明同一个变量
function fn(){
 var x = 123
 let x = 234 //报错
}
-----------------------------
function fn(){
 let x =123
 let x =234 //报错
}
-----------------------------
function fn(x){
 let x = 123
}
fn()//报错  因为重新声明了参数
-----------------------------
function fn(x){
 {let x = 123}
}//不报错

const

const声明一个只读的常量。一旦声明,常量的值就不能改变。const和let的作用域相同,只在块级作用域内有效,并且也不存在变量提升,同样存在暂时性死区,也不可以重复声明

const hi = 123
hi //123

hi = 234 //报错

也就是说用const定义的变量必须在定义的时候就进行复制 否则会报错

const a
a = 123 //报错

const实质上并不是保证定义变量的值不变 而是指向内存地址所保存的数据不可变,对于Number、String、Boolean这种简单的数据类型 值就会保存在指向的那个内存地址 所以相当于常量 对于复合型数据 对象、数组const保存的是指向实际数据的指针 所以对于复合型数据内容可不可变是不能保证的

const复合型数据  对象 数组

const foo = {}
foo.name = 'jack'  //成功
foo // foo{name : 'jack'}

const bar = {name : 'lisa'}
foo = bar //报错

从上面的例子可以看出 用const定义一个对象是可以在定义完后往里面添加新的数据,但是无法将新的对象赋值给原对象 数组也同理 看下面数组的例子

const arr = []
arr.push('study')
arr  // ['stydy']
arr.length = 0
arr.length // 0
arr = ['play'] //报错