ES6躬行记(1)——let和const

  古语云:“纸上得来终觉浅,绝知此事要躬行”。的确,无论看了多少本书,若是本身不实践,那么就很难领会其中的精髓。本身研读过许多ES6相关的书籍和资料,平时工做中也会用到,但在用到时常常须要上搜索引擎中查询相关知识概念,而且对不少知识也仅仅是略知一二,没有领会到其中的原理。为此,开辟了《ES6躬行记》系列,将ES6相关的知识系统的记录下来,以便本身翻阅,也但愿能帮助到广大网友。git

  在ES6以前的版本中,用于声明变量的关键字只有var,而且没有块级做用域,只有函数做用域和全局做用域,但在ES6中已改变这种情况。ES6引入了let和const两个关键字,它们既能够用于声明变量,还可以将变量绑定到当前所处的任意做用域中,换句话说,就是把变量的做用域封闭在所处的代码块(即花括号字符“{”和“}”之间的区域,例如if条件语句中的代码)中,如此一来就造成了块级做用域。let和const两个关键字与var之间的不一样,简单的说有如下三点:编程

(1)不容许声明提高。浏览器

(2)不容许重复声明。异步

(3)不覆盖全局变量。编程语言

  而在let与const之间也有不一样之处,在下面的内容中会重点讲解。模块化

1、let

  let能够理解为var的升级版本,摒弃或纠正了var的一些会致使代码混乱的特性,从而使得代码的逻辑更清晰,可维护性更高。函数

1)提高搜索引擎

  首先要介绍的是用let声明的变量,其声明语句不会再被提高。下面将两个变量分别用var和let声明,再输出它们的结果,具体以下所示。编码

console.log(outer);       //undefined
console.log(inner);       //抛出未定义的引用错误

{
  console.log(outer);     //undefined
  console.log(inner);     //抛出未定义的引用错误
  var outer = true;
  let inner = true;
  console.log(outer);     //true
  console.log(inner);     //true
}

console.log(outer);       //true
console.log(inner);       //抛出未定义的引用错误

  两个变量都在代码块中执行赋值操做。第一个outer变量因为是用var声明的,所以它的声明语句可以被提高,而在第一次和第二次输出时,由于还没被赋值,因此输出的结果都为undefined,在赋完值后,输出的结果就都是true。第二个inner变量是用let声明的,因为声明语句不能被提高到代码块的外部,所以它的做用域只限于代码块内,在代码块外就没法访问到它,而且在声明它以前也没法被访问(由于声明语句也不会被提高至做用域顶部),此时变量正处于临时死区中。若是强行访问,那么就会抛出未定义的引用错误。spa

  临时死区(Temporal Dead Zone,简称TDZ)也叫暂时性死区,用let或const声明的变量,在声明以前都会被放到TDZ中,而在此时访问这些变量就会触发运行时错误。这种语法设计能促进工程师们在平时养成良好的编码习惯,减小或杜绝一些因为始料未及的缘由而产生的程序BUG。有一点要注意,TDZ并非由ECMAScript标准命名的,它是JavaScript社区的一种约定俗成的叫法。

2)重复声明

  接下来介绍let的第二个特性:不容许重复声明。注意,这里有一个前置条件,那就是只有在相同做用域时,才不容许同一个变量重复声明。ES6引入的这个重复声明的检查机制,能够避免在多人协做开发的时候,因多声明一个或多个同名的变量,而影响别人代码逻辑的状况出现。下面的示例就分了两个做用域分别说明,而且对比了var和let对待重复声明的处理结果。

var duplicate;
let repeat;

var duplicate;        //var声明的变量可重复声明
let duplicate;        //抛出重复声明的语法错误
let repeat;           //抛出重复声明的语法错误
{
  let repeat;         //不一样做用域,可正常声明
}    

3)全局做用域

  最后介绍的是let在全局做用域中的特性。当用var在全局做用域中声明变量的时候,该变量不但会成为全局变量,并且还会成为全局对象(例如浏览器中的window对象)的一个属性。全局对象以及它的属性能够在任何位置被访问,这样就影响了函数的封装,不利于代码的模块化,而且新声明的全局变量有可能会覆盖全局对象中已存在的属性。上述是两个比较有表明性的弊端,为了解决这些问题,ES6规定用let可将全局变量和全局对象断开联系。下面用两组代码分别演示断开联系(第一组)和覆盖已有属性(第二组),注意,在第二组代码中为了方便对比,忽略了重复声明的错误。

//第一组
var global = true;
console.log(window.global);    //true
let whole = true;
console.log(window.whole);     //undefined

//第二组
var Math = true;
console.log(window.Math);      //true
let Math = true;
console.log(window.Math);      //Math对象

2、const

  const不但拥有上面所述的let的三个特性,而且还能声明一个常量。常量是指一个定义了初始值后固定不变的只读变量。在过去,常量都是经过命名规范(例如用大写字母、下画线等字符)定义的,虽然实现起来很便捷,但因为缺乏约束,所以这种常量很容易被修改。而在ES6引入了const关键字后,就能像其它编程语言那样声明常量了。有一点要注意,const与let不一样,在声明时必须初始化(即赋值),而且在设定后,其值没法再更改,以下代码所示。

const number;      //抛出未初始化的语法错误
const digit = 10;
digit = 20;        //抛出赋值给常量的类型错误

  此处要强调一点,const限制的实际上是变量与内存地址之间的绑定,也就是说,const让变量没法更改所对应的内存地址。若是是基本类型(例如布尔值、数字等)的变量,那么对应的内存地址中保存的就是值;若是是引用类型(例如对象)的变量,那么对应的内存地址中保存的是指向实际数据的一个指针。由此可知,当用const声明的变量,其初始化的值是对象时,能够修改对象中的属性或方法,具体可参考下面的代码。

const obj = {};
obj.name = "strick";
obj.age = function() {
  return 29;
};

3、循环中的let和const

  ES6规定了let声明在循环内部的行为,以for循环为例,以下所示。

for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 0);
}

  在控制台输出的结果依次为0、一、2,没有出现循环中的异步回调问题。这是由于在每次循环的时候,都会从新建立一个叫作i的同名变量,并将其初始化为计算后的值,而循环体内调用的i变量不会受其它同名变量的影响,因此可以在定时器的回调函数中正确显示该变量的值。在ES5及以前的版本中,若是要解决异步回调问题,就须要像下面这样借助当即执行函数表达式(IIFE)才能获得预期的效果。

for (var i = 0; i < 3; i++) {
  (function(n) {
    setTimeout(function() {
      console.log(n);
    }, 0);
  })(i);
}

  const声明在循环内部的行为与let声明相似,不过,因为其值没法修改,所以在for循环中从新赋值(例如执行增量操做)将会抛出异常。但只要避开从新赋值,就能正常循环迭代,例如用for-in执行const声明的循环,以下所示。

var author = {
  name: "strick",
  age: 29
};
for (const key in author) {
  console.log(key);
}
相关文章
相关标签/搜索