古语云:“纸上得来终觉浅,绝知此事要躬行”。的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓。自己研读过许多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. 从Excel表中导入数据时日期格式的验证问题解决

    #region IsDateTimeType 私有方法判断导入数据是否是日期格式 /// <summary> /// 私有方法判断导入数据是否是日期格式 /// </summary& ...

  2. openmp入门总结

    Ref: https://wdxtub.com/2016/03/20/openmp-guide/ 简介 这门课作为 ECE 中少有的跟计算机科学相关的课,自然是必上不可.不过无论是 OpenMP 还是 ...

  3. android-effect

    1. 基本框架 2.初探

  4. [转]MYSQL性能查看(命中率,慢查询)

    网上有很多的文章教怎么配置MySQL服务器,但考虑到服务器硬件配置的不同,具体应用的差别,那些文章的做法只能作为初步设置参考,我们需要根据自己的情况进行配置优化,好的做法是MySQL服务器稳定运行了一 ...

  5. JAVA课程设计---学生基本信息管理系统

    1.团队课程设计博客链接 http://www.cnblogs.com/zyjjj/p/7061880.html 2.个人负责模块或任务说明 函数 功能说明 Search 查找学生信息,分为两种查找方 ...

  6. vuex简单使用

    1.创建src/store/index.js----仓库所在地----暴露store 2.main.js入口文件处引入store,挂载到Vue根实例中 3.创建store/movie.js-----电 ...

  7. jQuery获取父级、兄弟节点的方法

    一.jQuery的父节点查找方法 $(selector).parent(selector):获取父节点 $(selector).parentNode:以node[]的形式存放父节点,如果没有父节点,则 ...

  8. [转] XEN, KVM, Libvirt and IPTables

    http://cooker.techsnail.com/index.php/XEN,_KVM,_Libvirt_and_IPTables XEN, KVM, Libvirt and IPTables ...

  9. c#图像处理入门(-bitmap类和图像像素值获取方法)

    c#图像处理入门 -bitmap类和图像像素值获取方法 一.Bitmap类 Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义 ...

  10. Python time库常用函数

    time模块中时间表现的格式主要有三种: timestamp 时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量 struct_time 时间元组,共有九个元素组. for ...