Skip to content

Latest commit

 

History

History
278 lines (185 loc) · 15.9 KB

2.使用ECMAScript6开发.md

File metadata and controls

278 lines (185 loc) · 15.9 KB

2. 使用ECMAScript6开发

转载请注明出处 https://brickcarvingartist.github.io/Setting-up-ES6/2.%E4%BD%BF%E7%94%A8ECMAScript6%E5%BC%80%E5%8F%91

这一章介绍在当前的JavaScript环境下开发ECMAScript 6你需要的操作。以下涵盖的都是备选的相关工具。如果你想要一个综合的工具清单,我建议你去Addy Osmani的“ECMAScript 6 Tools”看看。

2.1 现在使用ECMAScript 6

在现在使用ECMAScript你有什么样的选择?

  • 当前的JavaScript引擎(浏览器,Node.js,...)已经支持大部分的ES6。你可以通过以下方式来查看已支持哪些特性:
  • Kangax的“ECMAScript 6 compatibility table”。
  • William Kapke的“Node.js ES2015 Support”列出了在Kangax的表格中仅与Node相关的部分。
  • ES6 REPLs(交互式命令行)可以让你测试出小部分的代码。在本章中的一个部分列出了可用的选项。
  • 把ES6编译成ES5:特别声明,如果你需要考虑对历史遗留的引擎作支持,在相当长的一段时间里,将ES6编译成ES5是唯一可选操作。将源码编译成源码也可以称之为源码翻译。你可以在部署环境(静态地)和运行时(动态地)用源码翻译的方式来使用ES6。这一章会阐述如何让它运转,以及其他的既有ES6工具和库。

ES6是ES5的超集是一个可喜点,这意味着你所有的基于ES5的代码都是在ES6中生效的。因为你可以逐渐的去使用它,这也帮助你更快接受专属ES6的特性。

2.2 ES6 REPLs

市面上有很多REPLs(命令行)用来交互地与ES6互动。以下项目提供丰富的选择为在线编码提供互动的玩乐场所:

另外,Babel通过它的babel-node工具将对ES6特性的支持带到Node.js。

2.3 源码翻译工具

有三个必要的选择你需要为代码翻译工具准备:

  • 一个代码翻译器(为你的代码服务)
  • 一个包管理工具(用来安装已有的库)
  • 一个模块系统 (为了完整的应用)

需要注意的是这些选择都不全是独立存在的;不是每一个模块系统都可以和每一个其它包管理工具一起运作。下一章详细介绍这些选择中的每一项。

2.3.1 选择一个翻译器

一个翻译器能将ES6的代码翻译成ES5。有名的选择有:

  • Microsoft TypeScript:是一个基础的ECMAScript6+可配置的类型注解。
  • Google Traceur:是最具人气的ES6翻译器。是法语发音,/tʁa.sœʁ/;用英语的话接近地发音成“truh-SIR” (来源, 听法语母语的人对这个单词的发音)。
  • Babel:是一个更新的,事实上即将成为标准的ES6翻译器。Babel除了支持ES6以外,还支持React的JSX语法。发音为“babble”(想象一下澳大利亚的重音 - Babel的创造者,澳大利亚的Sebastian McKenzie)。
  • Closure Compiler:如果你用接下来的两个命令行选项,就能够当作例如ECMAScript6至ECMAScript5的静态翻译:
  • 指定输入语言:--language ECMASCRIPT6_STRICT
  • 指定输出语言:--language_out ECMASCRIPT5

原则上,翻译器以下面任意一种方式完成翻译:

  • 静态地(部署环境之前)或者
  • 动态地(在运行时)
2.3.1.1 静态翻译

作为一个构建步骤,TypeScript,Traceur,Babel和Closure Compiler让你在以下格式化模块中提供ES5代码。你可以任意直接得调用他们或者用一个构建工具(grunt,gulp,broccoli,或其它)。

  • AMD
  • CommonJS
  • ES6 module loader API:使用这个API会通过polyfill把ES6代码翻译成ES5。

在浏览器上,很多ES5模块都是通过等会儿介绍的模块系统之一来加载。在Node.js上,你可以用内置的模块系统(也存在其他的选择,比如webpack和ES6模块加载器polyfill)。

2.3.1.2 动态代码翻译

在浏览器上,你通过一个库加上一个定制的<script type="...">动态编译。这个操作在TraceurBabel上存在。

在Node.js上,Babel有工具可以进行在传输过程中的编译。这些会在另一个章节描述。

2.3.2 选择一个包管理工具

你需要一个包管理工具来安装第三方库。这是三个有名的:

  • npm:Node.js的与生俱来创建的包管理工具,在客户端开发中的受欢迎程度多亏像browserify和webpack这样的模块打包和加载工具得以持续增长。包含CommonJS模块的包和/或比如命令行工具的其它内容。
  • Bower:客户端代码的包管理工具。最知名的包是AMD模块,但是CommonJS模块,CSS,HTML和其它物件也能通过它管理。
  • jspm:SystemJS的包管理工具(看下一个bullet list)。它能通过多样的来源安装模块,包括Github和npm。jspm的一个关键特性是外部模块能用ES6书写(会被代码翻译)。不仅是你自己的模块。

2.3.3 选择一个模块系统

模块系统让浏览器支持模块化(Node.js有一个内置模块系统)。那样,你可以通过模块系统构建出你自己的应用和库模块。有名的模块系统有:

  • RequireJS:是一个AMD模块的加载器,它可以静态地通过TypeScript,Traceur,Babel和Closure Compiler创建。模块插件(基于Traceur和Babel)让它可以加载ES6模块。
  • Browserify:打包CommonJS模块(包括通过npm安装的那些模块)用以让它们通过浏览器加载。通过基于Traceur和Babel的转换器(插件)支持ES6模块。
  • webpack:一个CommonJS模块(包括通过npm安装的那些模块)或AMD模块(包括通过Bower安装的那些模块)都可以用的打包和加载工具。通过定制基于Traceur和Babel的加载器(插件)以支持ES6模块。
  • SystemJS:一个支持ES6模块及用CommonJS,AMD和“ES6 module loader API”格式书写的ES5模块,基于ES6模块加载器Polyfill的模块系统。

2.4 其它有用的ES6工具和库

2.4.1 Linters和检查器

Linters和检查器静态地分析源码并反馈与风格,方式等相关的问题。

下面的Linters都支持ES6,但是取自不同的角度:

  • JSLint(关注:强制代码在实践中规范书写)
  • JSLint(关注:强制代码在实践中规范书写)
  • ESLint(关注:允许人们用他们自己的风格实现规范)
  • JSCS(关注:强制代码风格)

下面的检查器都支持ES6:

  • Flow:会被类型检查的代码。它以以下三个来源获得类型信息:
  • 类型注解语法(非规范的)
  • 在注释中做类型注解
  • 类型接口(通过静态地统计源码进行类型推断,甚至完全没有注解的代码Flow都是有效的)
  • TypeScript:除了可以作为一个代码翻译器,TypeScript的编译器和也与Flow的运作方式类似并且对类型问题作警告。
  • Closure Compiler:会被类型检查的代码并且能理解存在JSDoc标记中的类型信息。

2.4.2 测试工具

很多测试工具(比如Jasmine和mocha)能被以最多得使用因为他们能与被代码翻译后的代码一起运作,所以他们没有必要去理解ES6的原始代码。Babel的文档中有关于如何去与非常多测试工具一起使用的信息。

2.4.3 Shims/polyfills

Shims/polyfills用ES5代码让你可以使用满足ECMAScript6规范的库:

2.4.4 ES6句法分析器

这里有一些可以处理ES6的句法分析器:

  • Esprima
  • Acorn
  • Babel最近已经成为不只是一个能静态分析和转换JavaScript源码的框架了。“Babel Plugin Handbook”(来自James Kyle)提供了部分关于Babel为什么能够运行的信息。

2.4.5 文档工具

这里有一些可以理解ES6的文档工具:

2.5 未来:本地化ES6

当第一个引擎完全支持ES6,直到所有不支持ES6的引擎淘汰,一个hybrid的实现可以被用在客户端的应用上:

  • 每个文件在服务器上都有两个版本:本地化ES6版本以及其代码翻译版,一个ES5版本。
  • 当应用启动时,特性侦查会习惯于检查ES6是否被完全支持。如果有,使用ES6版本的应用。反之,使用ES5的版本。

查清ECMAScript的版本是很难的,因为很多引擎在完全支持前会支持不同的版本的一些部分。比如说,这是你想要检查一个引擎是否支持ECMAScript6的for-of循环 - 但那能很好地确定引擎对这一个ES6特性的支持:

function isForOfSupported() {
	try {
		eval("for (var e of ['a']) {}");
		return true;
	} catch (e) {
		// 有可能性地:检查e是否是SyntaxError的实例
	}
	return false;
}

Kyle Simpson的ES Feature Tests让你可以侦查一个引擎是否支持ES6。

var ReflectSupports = require("es-feature-tests");

ReflectSupports("all", function (results) {
	if (results.letConst && results.arrow) {
		// 本地化使用ES6
	} else {
		// 使用代码翻译的ES6
	}
});

npm也许最终会支持同一个模块的两个版本,基于npm,你将能同时用Node.js和客户端模块程序的ES5和ES6版本发布库。

2.6 有没有不能被代码翻译为ES5的ES6特性呢?

有些ECMAScript6的特性不能代码翻译(编译)成ES5。ES6有三种类型的特性:

  • 已存在特性的更好语法表达
  • 标准程序库中的新功能
  • 全新的特性

下一节解释把这每一个特性用代码编译成ES5有多困难。

2.6.1 已存在特性的更好语法表达

ES6已经可以通过库使其提供的更好语法特性可用。两个例子:

  • 模块

都可以相对简单地编译成ES5。

2.6.2 标准程序库中的新功能

ES6有一个比ES5更广泛的标准。添加的内容包括:

  • 字符串,数组的新方法
  • Promises
  • Maps,Sets

这些都能通过一个库来提供。大部分的功能(比如String.prototype.repeat())对ES5来说简直有用。稍后一点的章节会列出一些这样的库。

2.6.3 全新的特性

ES6有一些与已存在的特性无关的特性。这些特性永远不可能如实得被代码翻译。不过它们中的一些可以有原因,比如: But some of them have reasonable simulations, for example:

  • letconst:被代码翻译为var并且必要的时候会将定义重命名来避免命名重复。这能进行快速的代码开发并且在实践中也能很好的运作,但是你不会得到常量在本地化ES6中所拥有的绑定不变性。
  • Symbols:被代码翻译为唯一的ID标识的对象。这些对象可以被用作属性的键,因为括号运算符可以将它们强转成字符串。此外,一些有遍历属性能力的方法(比如Object.keys())只能打上补丁来忽略那些作为键的被代码翻译的symbol。
  • Generators:被编译成状态机,是一个复杂的转换,却能够简直良好地运作。比如,这个generator方法:
	function* gen() {
		for(let i=0; i < 3; i++) {
			yield i;
		}
	}

被代码翻译成以下的ES5代码:

var marked0$0 = [gen].map(regeneratorRuntime.mark);
	function gen() {
		var i;
		return regeneratorRuntime.wrap(function gen$(context$1$0) {
			while (1) switch (context$1$0.prev = context$1$0.next) {
				case 0:
					i = 0;

				case 1:
					if (!(i < 3)) {
					  context$1$0.next = 7;
					  break;
					}

					context$1$0.next = 4;
					return i;

				case 4:
					i++;
					context$1$0.next = 1;
					break;

				case 7:
				case "end":
					return context$1$0.stop();
			}
		}, marked0$0[0], this);
	}

你可以看到代码中的状态机,下个状态被存储在了context$1$0.next中。

看看Facebook的regenerator库获取更多信息。

  • WeakMaps:整个WeakMap里都是键值对。键基本都是对象。WeakMap的最主要的特性就是键是弱连接:如果将一个对象作为键并且没有其他与它相关的(强)引用的话它是可以被垃圾回收的。一种使用ES5对WeakMap的模仿是一个对象包含一个唯一的ID(比如一个'weakmap_072c-4328-af75'的字符串),否则就是空的。它的每一个键值入口都被以值存在键上,通过一个属性名为WeakMap的ID管理起来。这确实相当偏门:这只会在键名可变时生效。再者,如果一个仿制的WeakMap被垃圾回收,它的那些在键中的属性没有移除,结果就是浪费内存。从好的层面来说,键都被仿制的WeakMap弱持有了。仿品能运作因为WeakMap的操作(getsethasdelete)都需要一个键。没有任何办法清除WeakMap或者去遍历它们的入口,键和值。

其他完全没可能用代码翻译的特性(我可是用了直接了当的语气):

  • Proxies:只有你让所有对象上的操作可截取,截取代理操作是唯一可能办法。这会导致一个极大的展现错误。
  • 内置子类的构造函数(比如ErrorArray)。通过一个新的ES5内置中不支持的子类协议可以让这些特性生效(new.target等)。用一个实现来代替它们真的不会是一个简单的解决方案,因为有很多地方都会用到内置构造函数,不要通过全局变量去涉及它。
  • 调用跟踪优化:普遍的实现调用跟踪优化在ES5里需要一个很激进得对ES6代码的转换(比如trampolining)。转换出来的结果代码是非常慢的。

感谢支持译者翻译

支持译者继续翻译


首页:架设ES6

上一章:关于本书

下一章:在浏览器和Node.js上安装Babel