ES6之let和const的区别

let:声明的是变量

一、不存在变量提高javascript

// var 的状况
console.log(foo); // 输出undefined
var foo = 2;

// let 的状况
console.log(bar); // 报错ReferenceError
let bar = 2;

上面代码中,变量foo用var声明,会发生变量提高,即脚本开始运行时,变量foo已经存在了,可是没有值,因此会输出undefined。变量用let声明,不会发生变量提高。这表示在声明它以前,变量bar是不存在的,这时若是用到他,就会抛出一个错误。java

二、暂时性死区,先声明在使用数组

var tmp = 123;

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

上面代码中,存在全局变量tmp,可是块级做用域内let又声明了一个局部变量tmp,致使后者绑定这个块级做用域,因此在let声明变量前,对tmp赋值报错,先声明再使用。函数

三、不容许重复声明spa

let不容许在相同做用域内,重复声明同一个变量。code

// 报错
function func() {
  let a = 10;
  var a = 1;
}

// 报错
function func() {
  let a = 10;
  let a = 1;
}

四、块级做用域对象

ES5只有全局做用域和函数做用域,没有块级做用域,这带来不少不合理的场景。ip

第一种场景,内层变量可能会覆盖外层变量作用域

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

上面代码,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。可是,函数f指向后,结果输出undefined,缘由在于变量提高,致使内层的tmp变量覆盖了外层的tmp变量。io

第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

上面代码中,变量i只用来控制循环,可是循环结束后,它并无消失,泄漏成了所有变量。

ES6的块级做用域,let实际上为javascript新增的块级做用域。

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

上边代码,有两个代码块,都声明了变量n,运行后输出5。这表示外层代码块不受内层代码块的影响。若是两次都使用var定义变量n,最后输出的值才是10。

ES6 容许块级做用域的任意嵌套。

{{{{{let insane = 'Hello World'}}}}};

const:常量

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const的做用域与let命令相同:只在声明所在的块级做用域内有效

const命令声明的常量也是不提高,一样存在暂时性死区,只能在声明的位置后面使用。

const声明的常量,也与let同样不可重复声明。

const foo = {};

// 为 foo 添加一个属性,能够成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另外一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另外一个地址,但对象自己是可变的,因此依然能够为其添加新属性

ES6 声明变量的六种方法

ES5 只有两种声明变量的方法:var命令和function命令。ES6 除了添加letconst命令,后面章节还会提到,另外两种声明变量的方法:import命令和class命令。因此,ES6 一共有 6 种声明变量的方法。

let和const的相同点:

① 只在声明所在的块级做用域内有效。

② 不提高,同时存在暂时性死区,只能在声明的位置后面使用。

③ 不可重复声明。

let和const的不一样点:

① let声明的变量能够改变,值和类型均可以改变;const声明的常量不能够改变,这意味着,const一旦声明,就必须当即初始化,不能之后再赋值

const i ; // 报错,一旦声明,就必须当即初始化
const j = 5;
j = 10; // 报错,常量不能够改变

② 数组和对象等复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const只保证变量名指向的地址不变,并不保证该地址的数据不变,因此将一个复合类型的变量声明为常量必须很是当心。

const arr = [];
// 报错,[1,2,3]与[]不是同一个地址
arr = [1,2,3];
const arr = [];
// 不报错,变量名arr指向的地址不变,只是数据改变
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr.length); // 输出:3

若想让定义的对象或数组的数据也不能改变,可使用object.freeze(arr)进行冻结。冻结指的是不能向这个对象或数组添加新的属性,不能修改已有属性的值,不能删除已有属性。

const arr = [];
Object.freeze(arr);
// 不报错,但数据改变无效
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
console.log(arr.length); // 输出:0