资讯专栏INFORMATION COLUMN

[译]ES6 模块化入门

shmily / 2180人阅读

摘要:当我们学习的模块化,就会发现它的发展深受的影响。严格模式在模块系统中,严格模式是默认开启的。同样的,模块内部的声明只在模块内部有效。在中,我们使用导入内容在模块中,我们只需要为导入的绑定起一个名字我们也可以导入具名导出的内容。

ES6 模块系统

在 ES6 之前,我们已经知道了 RequireJS,AngularJS 的依赖注入,以及 CommonJS,具体可以看笔者的上一篇文章《JS模块化历史简介》。当我们学习 ES6 的模块化,就会发现它的发展深受 CommonJS 的影响。通过这篇文章,我们将看到 exportimport 语句,以及 ES6 模块是怎么与 CommonJS 模块兼容的。

严格模式

在 ES6 模块系统中,严格模式是默认开启的。严格模式是语言从语法层面限制你使用一些不好的写法,所以它更严格(==)。它也让编译器更好地处理代码。下面是 MDN上关于严格模式的解释:严格模式

变量必须显式声明

函数的形参必须有唯一的名称(否则会报语法错误)

不能使用with

给只读的属性赋值会报错

00840 这样的八进制数字会报语法错误

试图 delete 无法删除的属性会报错

delete prop 会报语法错误,可以使用 delete global[prop]

eval 不会在所在的词法作用域引入新的变量

evalarguments 不能被改变或赋值

arguments 不会跟踪方法的参数变化

arguments.callee 不再支持,会报 TypeError

arguments.caller 不再支持,会报 TypeError

传入方法内部的 this 不再被强制转换成 Object

fn.callerfn.arguments 不再支持

保留关键字 protectedstaticinterface 不能被绑定

即使在 ES6 中严格默认是默认开启的,也推荐在每个模块中都使用 use strict 关键字。

让我们先来看下 export 关键字吧~

export

在 CommonJS 中,导出模块可以用 module.exports 。从下面的代码可以看出,你可以导出任何值:

module.exports = 1
module.export = NaN
module.exports = "foo" 
module.exports = { foo: "bar" } 
module.exports = ["foo", "bar"] 
module.exports = function foo () {}

像 CommonJS 模块一样,ES6 模块也是暴露 API 的文件。同样的,ES6 模块内部的声明只在模块内部有效。这就意味着,某个模块中的变量,如果没有被导出,在其他模块中就无法使用。

Exporting a Default Binding

上面的 CommonJS 代码如果用 ES6 语法写起来也很相似,主要就是将 module.exports 替换为 export default

export default 1 
export default NaN 
export default "foo" 
export default { foo: "bar" } 
export default ["foo", "bar"] 
export default function foo () {}

而与 CommonJS 不同的是,export 语句只能放在 ES6 模块代码的顶层,就算放在一个立即执行的函数中也不行。可想而知,这种限制让编译器更容易解析 ES6 模块,同时也让避免了在方法中动态导出这种不是很实用的骚操作。

function foo () { 
    export default "bar" // SyntaxError 
} 
foo()

导出语法不是只有 export default,你也可以使用具名导出。

Named Exports

在 CommonJS 中导出时也不是必须将 module.exports 赋值为一个对象,你可以直接改变它的属性。

module.exports.foo = "bar"
module.exports.baz = "ponyfoo"

好了,下面用 ES6 的写法也很简单(原文说了一堆不太重要就没翻~):

export var foo = "bar"
export var baz = "ponyfoo"

有一句话要始终牢记,我们导出的是绑定而不是值。

Bindings, Not Values

在 ES6 模块中重要的一个点是:导出的是绑定,而不是值或者引用。这就意味着你导出的变量foo 被绑定在了模块上,它的值改变了,外部也能收到变化。尽管通常情况下不推荐在模块加载后改变导出的值。

如果你有一个 ./a 模块,导出的 foo 将在 500ms 后从 bar 变为 baz

export var foo = "bar"
setTimeout(() => foo = "baz", 500)

除了默认绑定和具名绑定,我们还可以导出一个列表的绑定。

Exporting Lists

下面的代码可以看到,ES6 模块允许我们导出一个包含顶级成员的列表:

var foo = "ponyfoo"
var bar = "baz"
export { foo, bar }

如果你想改变其中变量的名字,可以用以下语法:

export { foo as ponyfoo }

同时也可以指定其中的某个为默认导出:

export { foo as default, bar }

而通常的最佳实践是只使用 export default,并且将其放在模块的底部。

Best Practices and export

如果同时使用命名导出,导出列表和默认导出,很容易造成困扰,所以大部分情况下作者建议只使用 export default,并且将语句放在模块文件的底部。你可以将要导出的对象叫作 api

var api = {
    foo: "bar",
    baz: "ponyfoo"
}
export default api

这样做有几个好处:(原文比较啰嗦,下面提炼两点)

将导出的内容包裹在一个对象中,在模块内部可以很容易找到导出的内容。

只使用 export default 具有一致性,不会因为过多导出方式造成混淆,在使用的时候也更加方便。

我们已经熟悉了 export 的 API 和注意事项,下面来看 import 语句。

import

作为与 export 相对的语句,import 可以让我们导入另一个模块中的内容。模块的加载方式,在浏览器端主要依靠 Babel 实现。而在内部 import 语句的实现也几乎是和 CommonJS 的 require 语句一样。

让我们用 lodash 来说明。下面的语句简单地加载了 Lodash 模块到我们自己的模块,它没有创建任何变量,但它将会执行 lodash 模块顶层代码的内容。

import "lodash"

在讲导入绑定之前,我们需要先明确的是,跟 export 语句类似,import 语句也只能在模块的顶层代码使用。这能让编译器更好地处理解析工作,也能帮助其他静态分析工具解析我们的代码。

Importing Default Exports

在 CommonJS 中,我们使用 require 导入内容:

var _ = require("lodash")

在 ES6 模块中,我们只需要为导入的绑定起一个名字:

import _ from "lodash"

我们也可以导入具名导出的内容。

Importing Named Exports

这个语法跟 ES6 的解构赋值很相似,但是也不太一样:

import { map, reduce } from "lodash"

跟解构赋值不同的其中一点是,你可以为导入的绑定创建别名,可以同时使用有别名和没有别名的导入。

import { cloneDeep as clone, map } from "lodash"

也可以同时使用具名和默认的导入,如果要在花括号内使用默认导入,需要使用 default 关键字,当然也可以给它起个别名,或者像第三行那样:

import { default, map } from "lodash"
import { default as _, map } from "lodash"
import _, { map } from "lodash"
import All The Things

我们也可以导入一个模块中的整个命名空间。import * 这个语法后面必须跟一个别名,这个别名中就存放了导入模块的所有绑定。如果里面包含一个默认导出,那么它放被放在了 alias.default

import * as _ from "lodash"
Conclusions

我们今天可以直接使用 ES6 模块,得益于 Babel 编译器借助了 CommonJS 模块的实现。这其中的一个好处就是 CommonJS 和 ES6 模块之间是兼容的,即我们可以在 ES6 模块中直接写 CommonJS 的语法。

ES6 模块系统看起来很棒,而它也是 JavaScript 中的一个最重要的功能。希望在不久的将来,模块加载 API 可以最终敲定并直接在浏览器端实现。

原文链接

欢迎关注我的公众号:码力全开(codingonfire)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105660.html

相关文章

  • 库,组件,框架 - 收藏集 - 掘金

    摘要:哈哈,我理解,架构就是骨架,如下图所示译年月个有趣的和库前端掘金我们创办的使命是让你及时的了解开发中最新最酷的趋势。 翻译 | 上手 Webpack ? 这篇就够了! - 掘金译者:小 boy (沪江前端开发工程师) 本文原创,转载请注明作者及出处。 原文地址:https://www.smashingmagazine.... JavaSrip... 读 Zepto 源码之代码结构 - ...

    tommego 评论0 收藏0
  • 即将立秋的《课多周刊》(第2期)

    摘要:即将立秋的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。课多周刊机器人运营中心是如何玩转起来的分享课多周刊是如何运营并坚持下来的。 即将立秋的《课多周刊》(第2期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大...

    ruicbAndroid 评论0 收藏0
  • 即将立秋的《课多周刊》(第2期)

    摘要:即将立秋的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。课多周刊机器人运营中心是如何玩转起来的分享课多周刊是如何运营并坚持下来的。 即将立秋的《课多周刊》(第2期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大...

    MRZYD 评论0 收藏0
  • 2017-10-01 前端日报

    2017-10-01 前端日报 精选 网页保存为图片及高清截图的优化方法前端最佳实践(一)——DOM操作Vue 2.0学习笔记:v-bindReact Router v4 之代码分割:从放弃到入门js实用的十个小技巧Netflix/falcor: A JavaScript library for efficient data fetchinglllyasviel/style2paints: ske...

    silencezwm 评论0 收藏0

发表评论

0条评论

shmily

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<