古语云:“纸上得来终觉浅,绝知此事要躬行”。的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓。自己研读过许多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. SpringBoot_定制banner

    SpringBoot项目在启动时会打印一个banner 这个banner 是可以定制的, 在resources 目录下创建一个banner.txt 文件,在这个文件中写入的文本将在项目启动时打印出来. ...

  2. 【repost】Python正则表达式

    星光海豚   python正则表达式详解 正则表达式是一个很强大的字符串处理工具,几乎任何关于字符串的操作都可以使用正则表达式来完成,作为一个爬虫工作者,每天和字符串打交道,正则表达式更是不可或缺的技 ...

  3. Freeradius服务器的搭建流程

    Freeradius服务器的搭建流程 一.服务器方面的配置 1 .安装radius服务器,数据库扩展插件 预先安装mysql数据库,然后安装freeradius,以及freeradius的数据库扩展插 ...

  4. pom.xml文件模板、application文件模板、configuration逆向生成文件、

    pom: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http:// ...

  5. Python学习过程中各个难点---函数篇

    对于函数,我一直分不清局部变量与全局变量,今天又好好研究了下,终于搞清楚了. 例子: 其次对于global这个关键字我也是一知半解的状态,之前整个人都是懵懵的,现在搞明白了 匿名函数: 匿名函数使用关 ...

  6. Debian 9.x "stretch" 安装 vnStat 统计服务器流量

    vnStat 是一款开源的 Linux 下统计网卡流量的软件,可以很方便地查看当前.当天.当月的流量统计报告,下面我们介绍下在 Debian 9.x 下安装 vnstat 的简单方法 首先,使用 ip ...

  7. PyCharm远程开发和调试

    PyCharm进行远程开发 1.背景 在Windows开发Python或Web应用程序,但是它们最后需要在linux上面来运行 2.解决办法 2.1 使用PyCharm内置支持的Vagrant 2.2 ...

  8. 不停止nginx服务,使配置文件生效

    ps -ef | grep "nginx: master process" | grep -v "grep" | awk -F ' ' '{print $2}' ...

  9. uintAPi 之Renderer.material

    Renderer.material public Material material; Returns the first instantiated Material assigned to the ...

  10. MySQL--Double Write

    ##=======================================##目前大部分服务器使用4K或512B来格式化磁盘,而Innodb存储引擎使用默认16K的数据页,在写入16KB数据页 ...