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

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

(1)不允许声明提升。

(2)不允许重复声明。

(3)不覆盖全局变量。

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

一、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声明的,由于声明语句不能被提升到代码块的外部,因此它的作用域只限于代码块内,在代码块外就无法访问到它,并且在声明它之前也无法被访问(因为声明语句也不会被提升至作用域顶部),此时变量正处于临时死区中。如果强行访问,那么就会抛出未定义的引用错误。

  临时死区(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对象

二、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;
};

三、循环中的let和const

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

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

  在控制台输出的结果依次为0、1、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);
}

ES6躬行记(1)——let和const的更多相关文章

  1. ES6躬行记 笔记

    ES6躬行记(18)--迭代器 要实现以下接口## next() ,return,throw 可以用for-of保证迭代对象的正确性 例如 var str = "向

  2. ES6躬行记(7)——代码模块化

    在ES6之前,由于ECMAScript不具备模块化管理的能力,因此往往需要借助第三方类库(例如遵守AMD规范的RequireJS或遵循CMD规范的SeaJS等)才能实现模块加载.而自从ES6引入了模块 ...

  3. ES6躬行记(21)——类的继承

    ES6的继承依然是基于原型的继承,但语法更为简洁清晰.通过一个extends关键字,就能描述两个类之间的继承关系(如下代码所示),在此关键字之前的Man是子类(即派生类),而在其之后的People是父 ...

  4. ES6躬行记(14)——函数

    在前面的章节中,已陆陆续续介绍了ES6为改良函数而引入的几个新特性,本章将会继续讲解ES6对函数的其余改进,包括默认参数.元属性.块级函数和箭头函数等. 一.默认参数 在ES5时代,只能在函数体中定义 ...

  5. ES6躬行记(20)——类

    ES6正式将类(Class)的概念在语法层面标准化,今后不必再用构造函数模拟类的行为.而ES6引入的类本质上只是个语法糖(即代码更为简洁.语义更为清晰),其大部分功能(例如继承.封装和复用等)均可在E ...

  6. ES6躬行记(13)——类型化数组

    类型化数组(Typed Array)是一种处理二进制数据的特殊数组,它可像C语言那样直接操纵字节,不过得先用ArrayBuffer对象创建数组缓冲区(Array Buffer),再映射到指定格式的视图 ...

  7. ES6躬行记(3)——解构

    解构(destructuring)是一种赋值语法,可从数组中提取元素或从对象中提取属性,将其值赋给对应的变量或另一个对象的属性.解构地目的是简化提取数据的过程,增强代码的可读性.有两种解构语法,分别是 ...

  8. ES6躬行记(4)——模板字面量

    模板字面量(Template Literal)是一种能够嵌入表达式的格式化字符串,有别于普通字符串,它使用反引号(`)包裹字符序列,而不是双引号或单引号.模板字面量包含特定形式的占位符(${expre ...

  9. ES6躬行记(15)——箭头函数和尾调用优化

    一.箭头函数 箭头函数(Arrow Function)是ES6提供的一个很实用的新功能,与普通函数相比,不但在语法上更为简洁,而且在使用时也有更多注意点,下面列出了其中的三点: (1)由于不能作为构造 ...

随机推荐

  1. web安全系列1:入侵的途径

    大家好,接下来的很长一段时间我都会介绍和web安全有关的知识,欢迎大家关注和转发. 话不多说,我们首先来看看今天的主题----入侵的途径.当然,今天介绍的都是针对web网站的常用手法和技巧. 不可否认 ...

  2. ehcache如何配置

    1.pom.xml文件配置(主要针对jar包的引入) <ehcache.version>2.6.9</ehcache.version><ehcache-web.versi ...

  3. MFC中添加控制台输出

    可以在CWinApp的InitInstance()中调用下面的函数,以生成控制台: #include <io.h> #include <fcntl.h> void InitCo ...

  4. C++标准库之string返回值研究

    先说结论(不一定适用所有环境): 1) GCC默认开启了返回值优化(RVO),除非编译时指定“-fno-elide-constructors”: 2) 现代C++编译器一般都支持返回值优化: 3) s ...

  5. 个人 WPF+EF(DBFirst) 简单应用开发习惯及EF学习测试(备忘) -- 2

    接上篇:个人 WPF+EF(DBFirst) 简单应用开发习惯及EF学习测试(备忘) -- 1 Step1 在主程序中设置连接数据库 从Model类库的 App.Config 把数据库字符串拷贝出来, ...

  6. docker容器下mysql更改WordPress的site address和home(URL)------局域网

    先简单介绍下,用docker安装的WordPress,mysql是在docker容器中的,并未在Ubuntu(我把WordPress是安装Ubuntu系统上),即WordPress和Ubuntu是独立 ...

  7. Python3.* 和Python2.*的区别

    许多Python初学者都会问:我应该学习哪个版本的Python.对于这个问题,我的回答通常是“先选择一个最适合你的Python教程,教程中使用哪个版本的Python,你就用那个版本.等学得差不多了,再 ...

  8. 常用类:Object

    2017-08-08 Object :作为所有类的根类,(超类,父类) 常用的方法: public int hasCode(){//返回该对象的哈希码值(地址)}:判断对象是否在同一内存地址上 pub ...

  9. MapReduce实例学习

    https://blog.csdn.net/m0_37739193/article/details/77676859

  10. Docker基础知识介绍

    本节内容 1.  Docker概述 2.  Docker的安装 3.  Docker基本使用 4.  Docker相关命令汇总 5.  Docker概念理解 一  Docker概述 Docker是什么 ...