编程风格
本章探讨如何将 ES6 的新语法,运用到编码实践之中,与传统的 JavaScript
语法结合在一起,写出合理的、易于阅读和维护的代码。
多家公司和组织已经公开了它们的风格规范,下面的内容主要参考了 Airbnb 公司的 JavaScript
风格规范。
块级作用域
(1)let 取代 var
ES6
提出了两个新的声明变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而且let没有副作用。
123456789'use strict';if (true) { let x = 'hello';}for (let i = 0; i < 10; i++) { console.log(i);}
上面代码如果用var替代let,实际上就声明了两个全局变量,这显然不是本意。变量应该只在其声明的代码块内有效,var命令做不到这一点。
var命令存在变量提升效用,let命令没有这个问题。
123456'use strict';if (true) ...
Module 的加载实现
上一章介绍了模块的语法,本章介绍如何在浏览器和 Node.js 之中加载 ES6
模块,以及实际开发中经常遇到的一些问题(比如循环加载)。
浏览器加载
传统方法
HTML 网页中,浏览器通过<script>标签加载 JavaScript
脚本。
12345678<!-- 页面内嵌的脚本 --><script type="application/javascript"> // module code</script><!-- 外部脚本 --><script type="application/javascript" src="path/to/myModule.js"></script>
上面代码中,由于浏览器脚本的默认语言是
JavaScript,因此type="application/javascript"可以省略。
默认情况下,浏览器是同步加载 JavaScript
脚本,即渲染引擎遇到<script> ...
Module 的语法
概述
历史上,JavaScript
一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如
Ruby 的require、Python 的import,甚至就连 CSS
都有@import,但是 JavaScript
任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD
两种。前者用于服务器,后者用于浏览器。ES6
在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代
CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6
模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS
和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS
模块就是对象,输入时必须查找对象属性。
12345678// CommonJS模块let { stat, exists, readfile } ...
Class 的继承
简介
Class 可以通过extends关键字实现继承,这比 ES5
的通过修改原型链实现继承,要清晰和方便很多。
12345class Point {}class ColorPoint extends Point {}
上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。
12345678910class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toS ...
Class 的基本语法
简介
类的由来
JavaScript
语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
12345678910function Point(x, y) { this.x = x; this.y = y;}Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')';};var p = new Point(1, 2);
上面这种写法跟传统的面向对象语言(比如 C++ 和
Java)差异很大,很容易让新学习这门语言的程序员感到困惑。
ES6 提供了更接近传统语言的写法,引入了
Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6
的class可以看作只是一个语法糖,它的绝大部分功能,ES5
都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用
ES6 的cl ...
async 函数
含义
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
前文有一个 Generator 函数,依次读取两个文件。
1234567891011121314151617const fs = require('fs');const readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error) return reject(error); resolve(data); }); });};const gen = function* () { const f1 = yield readFile('/etc/fstab'); co ...
Generator 函数的异步应用
异步编程对 JavaScript 语言太重要。JavaScript
语言的执行环境是“单线程”的,如果没有异步编程,根本没法用,非卡死不可。本章主要介绍
Generator 函数如何完成异步操作。
传统方法
ES6 诞生以前,异步编程的方法,大概有下面四种。
回调函数
事件监听
发布/订阅
Promise 对象
Generator 函数将 JavaScript 异步编程带入了一个全新的阶段。
基本概念
异步
所谓“异步“,简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。
比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步。
相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。
回调函数
JavaScript
语言对异步编程的实现,就是回调函数。所谓回调函 ...
Generator 函数的语法
简介
基本概念
Generator 函数是 ES6
提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍
Generator 函数的语法和 API,它的异步编程应用请看《Generator
函数的异步应用》一章。
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator
函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator
函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历
Generator 函数内部的每一个状态。
形式上,Generator
函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
1234567function* helloWorldGenerator() { yield 'hello'; yield 'world' ...
Iterator 和 for…of 循环
Iterator(遍历器)的概念
JavaScript
原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6
又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署
Iterator
接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator
的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是
ES6 创造了一种新的遍历命令for...of循环,Iterator
接口主要供for...of消费。
Iterator 的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将 ...
Promise 对象
Promise 的含义
Promise
是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6
将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise
是一个对象,从它可以获取异步操作的消息。Promise 提供统一的
API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pen ...
