深入ES6 模块系统
深入ES6 模块系统
本文转载自:众成翻译
译者:neck
链接:http://www.zcfy.cc/article/4436
原文:https://ponyfoo.com/articles/es6-modules-in-depth#the-es6-module-system
ES6 模块系统
在ES6之前,我们用自己的方式来在 JavaScript 中实现模块。很长一段时间以来,像 RequireJS、Angular 的依赖注入和 CommonJS 这样的系统,配合着一些有用的工具,比如 Browserify 和 Webpack,一直在解决我们的需求。然而,到了2015 年,一个标准的模块系统早就应该发布了。我们马上就会看到,你很快会注意到 ES6 模块受到了 CommonJS 的很大影响。我们将查看export 和 import语句,从中会看到ES6模块和CommonJS有多一致,同时,我们将会在这篇文章中讨论它们。
今天我们将介绍 ES6 模块系统的几个方面。
[严格模式](# 严格模式)
[
export最佳实践](#export最佳实践)-
[导入具名的 Exports](#导入具名的 Exports)
[
import所有内容](#import所有内容)
严格模式
在 ES6 模块系统中, 严格模式默认被开启。如果你不知道严格模式是什么, 它只是语言的一个更严格的版本它让语言的很多不好的部分都消失了。它使编译器可以通过在用户代码中禁止使用一些不可靠的语法来表现得更好。下面是对 MDN 上的严格模式文章中所记录的更改的总结。
变量不能未声明就使用
函数参数必须有唯一的名称 (否则会被认为是语法错误)
with语句被禁止使用赋值给只读属性会抛出一个错误
像
00840这样的八进制数是语法错误尝试
delete不可删除的数据会抛出一个错误delete prop被认为是语法错误, 只能删除属性delete global[prop]eval不会引入新的变量到它的作用域eval和arguments的绑定不会被改变arguments不会神奇地跟踪方法参数的变化不再支持
arguments.callee,使用它会抛出TypeError不再支持
arguments.caller,使用它会抛出TypeError上下文作为
this在方法调用时不会被强制包装成一个Object(译者注:即this不会指向全局对象)不再能够使用
fn.callerandfn.arguments访问 JavaScript 的堆栈保留字(例如
protected,static,interface等等)不能被作为新变量声明
如果这些规则对你来说不是显而易见的,你应该使用 'use strict' 在每一个地方。尽管在 ES6 中已经成为事实,但在 ES6 中使用 'use strict' 仍然是一种很好的做法。我已经使用严格模式很长时间了,并且绝不会用回原来的模式!
现在让我们了解export,我们的第一个 ES6 模块关键字!
export
在 CommonJS 中,你将值暴露在module.exports上来导出它们。正如下面的代码片段所示,您可以导出任何内容像是基本类型、对象、数组或函数。
module.exports = 1
module.exports = NaN
module.exports = 'foo'
module.exports = { foo: 'bar' }
module.exports = ['foo', 'bar']
module.exports = function foo () {}
ES6模块系统将 export封装成API,类似于 CommonJS的modules。ES6 模块中的声明只作用于该模块,和使用 CommonJS 一样。这意味着,在模块中声明的任何变量都不能用于其他模块,除非它们明确地导出为模块 API 的一部分(然后导入到希望访问它们的模块中)。
导出默认的绑定
你可以通过把 module.exports = 变成 export default来模拟我们刚刚看到的CommonJS代码。
export default 1
export default NaN
export default 'foo'
export default { foo: 'bar' }
export default ['foo', 'bar']
export default function foo () {}
与 CommonJS 不同,导出语句只能放在 ES6 模块的最外层,而不能放在方法中,即使在加载模块时它们所在的方法会立即被调用。据推测,这种限制是为了让编译器更容易地解释 ES6 模块,但是这也是一个很好的限制,因为有很多很好的理由去以动态地定义和暴露 API的方式来调用方法。
function foo () {
export default 'bar' // SyntaxError
}
foo()
你不只可以使用默认的Export,你还可以使用具名的Exports。
具名的Exports
在 CommonJS 中,你甚至不需要事先分配一个对象给 module.exports。你可以把属性添加到它上面。不管 module.exports最终的属性包含什么,它仍然是一个单独的绑定。
module.exports.foo = 'bar'
module.exports.baz = 'ponyfoo'
我们可以通过使用具名导出语法在 ES6 模块中复制上述内容,而不是像CommonJS一样将它分配给module.exports。在ES6中,你可以声明要export的绑定。注意,下面的代码不能重构为先声明变量再执行 export foo,那将会导致一个语法错误。在这里,我们看到了ES6模块如何通过声明式模块系统API的工作方式来支持静态分析。
export var foo = 'bar'
export var baz = 'ponyfoo'
还有一个重要的点,是要记住我们正在导出的是绑定。
是绑定,而不是值
重要的一点是,ES6 模块导出的是绑定,而不是值或引用。这意味着您导出的foo 变量将被绑定到模块上的foo 变量中,它的值将取决于对foo的修改。 不过,我建议在最初加载模块之后,不要更改模块的公共接口。
如果你有一个./a模块像下面这样,这导出的foo将被绑定为'bar',持续500ms之后,foo将绑定为 'baz'
export var foo = 'bar'
setTimeout(() => foo = 'baz', 500)
除了默认绑定和单独绑定之外,你还可以导出一个绑定列表。
绑定列表
正如下面的代码片段所示,ES6 模块允许你导出已命名的位于顶级作用域的成员列表。
var foo = 'ponyfoo'
var bar = 'baz'
export { foo, bar }
如果你想要用其他名字来导出一个绑定,你可以使用export { foo as bar }语句,就像下面展示的这样。
`export { foo as ponyfoo }`
在使用export的命名成员列表声明风格时,还可以使用as default 。下面代码的作用和执行export default foo和export bar 一样,只不过在一行语句而已。
`export { foo as default, bar }`
只在模块文件的底部使用export default有很多好处。
export最佳实践
可以定义具名的Exports,可以导出一个具有别名的列表,还可以暴露一个默认的export,这会导致一些混乱。在很大程度上,我鼓励你们使用export default并且最好在模块文件的末尾使用。如下代码所示,你可以调用你的API 对象 api 或者将它命名为模块本身。
var api = {
foo: 'bar',
baz: 'ponyfoo'
}
export default api
第一,模块的导出接口立即变得明显。无需在模块中翻查并将各个部分组合在一起来计算 API,您只需滚动到最后。有一个清晰定义的 API 导出的地方,也可以更容易地解释模块导出的方法和属性。
第二,是应该使用 export default还是具名的导出又或者是列表的导出甚至是带有别名的导出,你不应该纠结这个。现在有一个指导方针,就是在任何地方都使用 export default。
第三,一致性。 在CommonJS世界中,我们通常从模块中导出一个方法,然后就可以了。而使用具名导出进行这样的操作是不可能的,因为你暴露了一个对象来表示该方法,除非你在导出列表中使用as default。
第四,这实际上是之前所提到的点的总结。export default 语句放在模块的底部,我们立即可以很清晰的看出这个模块的API是什么、有哪些方法,可以让模块的使用者可以很轻松的调用它的 API。当习惯于使用export default并总是在模块的最后使用它,你会感到使用ES6的模块系统是无痛的。
现在我们已经讨论了export API 及其注意事项,让我们开始讨论 import 语句。
import
这个语句是和export相对的语句。首先,它们可以被用来从另一个模块加载一个模块,这种加载模块的方式是特别实现的,目前还没有浏览器实现模块加载。聪明的人会在浏览器中解决模块加载问题,这样,你就可以立即编写符合标准的 ES6 代码。像 Babel 这样的转换工具可以在模块系统的帮助下像CommonJS一样连接模块。意味着在babel中,import语句和CommonJS中的require语句遵循一样的语义。
让我们以 lodash 为例。下面的语句简单地从模块中加载 Lodash 模块。它并没有创建任何变量,但它将可以使用lodash 模块。
`import 'lodash'`
在导入绑定之前,让我们来关注一下import 语句的实际情况。和export 很像,它只能定义在模块的顶级作用域。这可以帮助转换工具实现它们的模块加载功能,并帮助其它静态分析工具解析你的代码库。
导入默认的Exports
在CommonJS中,你可以通过 require语句import一些代码,就像这样:
`var _ = require('lodash')`
要从ES6模块导入默认的导出绑定,你只需要为它指定一个名字。与声明一个变量相比,语法有点不同,因为你正在导入一个绑定,而且可以让它更利于静态分析工具的分析。
`import _ from 'lodash'`
你也可以导入具名的导出并且可以使用别名。
导入具名的导出
这里的语法和我们刚才使用的默认导出非常相似,只需添加一些大括号,然后选择任意指定的导出. 注意,这个语法类似于解构赋值语法,但也有一些不同。
`import {map, reduce} from 'lodash'`
不同于解构赋值的是,你可以使用别名来重命名导入的绑定。你可以在你认为合适的情况下混合使用别名和非别名的导出。
`import {cloneDeep as clone, map} from 'lodash'`
你还可以混合和匹配指定的导出和默认导出。如果你想要它在括号里,你必须使用default的名称,你可以为default指定别名;或者你也可以将默认的导入与指定的导入列表混合在一起。
import {default, map} from 'lodash'
import {default as _, map} from 'lodash'
import _, {map} from 'lodash'
最后,还有import *的语句
import 所有内容
你还可以将一个模块导入为命名空间对象。它不导入指定的导出或默认值,而是导入所有的东西。注意,导入语法必须使用别名,其中所有绑定都将被替换到别名上。如果有一个默认的导出,将会被替换为alias.default。
`import * as _ from 'lodash'`
上面的代码展示了这个语法。
结论
注意,你可以在利用CommonJS模块的同时,通过babel编译器来使用ES6模块。最重要的是,你可以在CommonJS和ES6模块之间进行互操作。这意味着即使你导入了一个用CommonJs编写的模块,它也会起作用。
ES6模块系统看起来很棒,它是JavaScript中缺少的最重要的东西之一。我希望他们能很快找到一个最终完成的模块加载API和浏览器实现。你可以从一个模块中export 或import绑定的多种方法,但这并不多,因为它们增加了复杂性,但是时间将会告诉你,所有额外的API是否和它的庞大一样方便。
深入ES6 模块系统的更多相关文章
- NodeJS模块和ES6模块系统语法及注意点
社区模块规范: 1.CommonJS规范 规范实现者: NodeJS 服务端 Browserify 浏览器 2.AMD规范 全称 异步模块定义 规范实现者: RequireJS 浏览器 3.CMD规范 ...
- Typescript 实战 --- (9)ES6与CommonJS的模块系统
1.ES6模块系统 1-1.export 导出 (1).单独导出 // a.ts export let a = 1; (2).批量导出 // a.ts let b = 2; let c = 3; ex ...
- 全面解析ECMAScript 6模块系统
快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中. <HTML开发Mac OS App 视频教程> 土豆网同步更新:http: ...
- ES6 的模块系统
原文地址:https://hacks.mozilla.org/2015/08/es6-in-depth-modules/ ES6 是 ECMAScript 第 6 版本的简称,这是新一代的 JavaS ...
- ES6中export , export default , import模块系统总结
最近在学习使用Webpack3的时候发现,它已经可以在不使用babel的情况下使用ES6的模块加载功能了. 说到ES6的模块加载功能,我们先复习一下CommonJS规范吧: 一 . CommonJS ...
- ES6模块import细节
写在前面,目前浏览器对ES6的import支持还不是很好,需要用bable转译. ES6引入外部模块分两种情况: 1.导入外部的变量或函数等: import {firstName, lastName, ...
- ES6模块的import和export用法总结
ES6之前以前出现了js模块加载的方案,最主要的是CommonJS和AMD规范.commonjs前者主要应用于服务器,实现同步加载,如nodejs.AMD规范应用于浏览器,如requirejs,为异步 ...
- commonjs模块和es6模块的区别
commonjs模块与es6模块的区别 到目前为止,已经实习了3个月的时间了.最近在面试,在面试题里面有题目涉及到模块循环加载的知识.趁着这个机会,将commonjs模块与es6模块之间一些重要的的区 ...
- ES6模块之export和import详解
ES6中的模块即使一个包含JS代码的文件,在这个模块中所有的变量都是对其他模块不可见的,除非我们导出它.ES6的模块系统大致分为导出(export)和导入(import)两个模块. 1.模块导出(ex ...
随机推荐
- window path 的基本配置
%JAVA_HOME%\bin;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\ ...
- lvs负载均衡net模式
环境配置,一台双网卡的ens33,ens37,ens37的网关是ens33的IP,指定一下nginx ens33,192.168.30.22,ens37,172.16.1.1nginx 192.16 ...
- JQuery的click,trigger触发a标签的click事件无效的问题分析
今天在做一个手机端webAPP链接下载的时候,给a标签一个下载链接,但是通过 <a id="downFile" download="" href=&quo ...
- Day3 分支结构
if语句的使用 在Python中,要构造分支结构可以使用if.elif和else关键字.所谓关键字就是有特殊含义的单词,像if和else就是专门用于构造分支结构的关键字,很显然你不能够使用它作为变量名 ...
- 为什么多 TCP 连接比单 TCP 连接传输快
转自: 我观察到,客户端机器从单一服务器使用 HTTP 下载一个文件:1. 单连接下载,速度没有达到客户端网络的最大带宽:2. 多连接同时下载,传输速度有极大的提高,带宽被占满. 假设如下前提:1. ...
- 关于node对文件的读取
设计: 通过终端git / cmd 获取用户输入路径,然后遍历路径下所有的文件,打印输出. 因为需要命令行交互,所以引入prompt库 (https://github.com/flatiron/pro ...
- PY简易爬虫
然而,实用性很差,仅仅是能用而已. 已知bug: 由于土啬的问题,经常会炸掉.网络不稳定导致各种Connection Aborted/SSLError: EOF occurred in violati ...
- [Usaco2004 Open]Cube Stacking 方块游戏
题面: 约翰和贝茜在玩一个方块游戏.编号为1到n的n(1≤n≤30000)个方块正放在地上.每个构成一个立方柱. 游戏开始后,约翰会给贝茜发出P(1≤P≤100000)个指令.指令有两种 ...
- [CodeForces]1042D
大意:求一个序列有几个子序列的和小于给定值,里面的数有正有负,序列长度≤200000. 列个式子,其实求的是sum[r]-sum[l-1]<T sum[r]-T<sum[l-1] 所以我们 ...
- 继续聊WPF——自定义滚动条
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winf ...