最新提案
本章介绍一些尚未进入标准、但很有希望的最新提案。
do 表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。
1234{ let t = f(); t = t * t + 1;}
上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到t的值,因为块级作用域不返回值,除非t是全局变量。
现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上do,使它变为do表达式,然后就会返回内部最后执行的表达式的值。
1234let x = do { let t = f(); t * t + 1;};
上面代码中,变量x会得到整个块级作用域的返回值(t * t + 1)。
do表达式的逻辑非常简单:封装的是什么,就会返回什么。
12345// 等同于 <表达式>do { <表达式>; }// 等同于 <语句>do { <语句> }
do表达式的好处是可以封装多个语句, ...
ArrayBuffer
ArrayBuffer对象、TypedArray视图和DataView视图是
JavaScript
操作二进制数据的一个接口。这些对象早就存在,属于独立的规格(2011 年 2
月发布),ES6 将它们纳入了 ECMAScript
规格,并且增加了新的方法。它们都是以数组的语法处理二进制数据,所以统称为二进制数组。
这个接口的原始设计目的,与 WebGL 项目有关。所谓
WebGL,就是指浏览器与显卡之间的通信接口,为了满足 JavaScript
与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个
32 位整数,两端的 JavaScript
脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像 C
语言那样,直接操作字节,将 4 个字节的 32
位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。
二进制数组就是在这种背景下诞生的。它很像 C
语言的数组,允许开发者以数组下标的形式,直接操作内存,大大增强了
JavaScript 处理二进制数据的能力,使得开发者有可能通过 Ja ...
异步遍历器
同步遍历器的问题
《遍历器》一章说过,Iterator
接口是一种数据遍历的协议,只要调用遍历器对象的next方法,就会得到一个对象,表示当前遍历指针所在的那个位置的信息。next方法返回的对象的结构是{value, done},其中value表示当前的数据的值,done是一个布尔值,表示遍历是否结束。
12345678910111213141516function idMaker() { let index = 0; return { next: function() { return { value: index++, done: false }; } };}const it = idMaker();it.next().value // 0it.next().value // 1it.next().value // 2// ...
上面代码中,变量it是一个遍历器(iterator)。每次调用it.next()方法,就返回一个对象,表示当前遍 ...
读懂 ECMAScript 规格
概述
规格文件是计算机语言的官方标准,详细描述语法规则和实现方法。
一般来说,没有必要阅读规格,除非你要写编译器。因为规格写得非常抽象和精炼,又缺乏实例,不容易理解,而且对于解决实际的应用问题,帮助不大。但是,如果你遇到疑难的语法问题,实在找不到答案,这时可以去查看规格文件,了解语言标准是怎么说的。规格是解决问题的“最后一招”。
这对 JavaScript
语言很有必要。因为它的使用场景复杂,语法规则不统一,例外很多,各种运行环境的行为不一致,导致奇怪的语法问题层出不穷,任何语法书都不可能囊括所有情况。查看规格,不失为一种解决语法问题的最可靠、最权威的终极方法。
本章介绍如何读懂 ECMAScript 6 的规格文件。
ECMAScript 6 的规格,可以在 ECMA 国际标准组织的官方网站(www.ecma-international.org/ecma-262/6.0/)免费下载和在线阅读。
这个规格文件相当庞大,一共有 26 章,A4 打印的话,足足有 545
页。它的特点就是规定得非常细致,每一个语法行为、每一个函数的实现都做了详尽的清晰的描述 ...
编程风格
本章探讨如何将 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 ...