概述
ES6 之前,社区定制了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于 node 服务器,后者用于浏览器。ES6 在语言标准的层面上实现了模块功能,而且相对简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器的通用模块解决方案。
ES6 的模块设计思想是尽量的静态化,使得编译时就能嫩嫩个确定模块的依赖关系,以及输入输出的变量。而 CommonJS 和 AMD 则都是在运行时确定这些东西,导致没办法在编译时做静态优化。
严格模式
ES6 的模块自动采用严格模式,不管有没有在模块头部加上 ‘use strict’。
详情见:严格模式和非严格模式
export 命令
模块功能主要由两个命令组成:export 和 import。export 用于定义模块对外暴露的接口,import 用于输入其它模块提供的接口。
- export 基本用法
一个模块就是一个独立的文件。该文件内部的所有变量、方法和类型外部默认都无法获取,必须通过 export 关键字输出以后才能在外部使用。下面两种写法都可以:
1 | // profile.js |
或者:
1 | // profile.js |
推荐使用后面这种写法,这样就可以在文件底部一眼看清楚输出了哪些变量。
- export as
export 输出的变量默认就是本来的名字,但也可以使用 as 关键字重命名:
1 | export { |
- export 对外接口要合内部变量一一对应
下面的写法会报错:
1 | // 报错 |
正确的写法是下面这样:
1 | // 写法一 |
- export 输出的接口与对应的值动态绑定
如果模块内修改了 export 输出的变量,外部获取到的值也会更新。
而 CommonJS 输出的值是缓存,不存在动态更新。
- export 可以放在模块顶层的任何位置
只要别放在块级作用域内,可以放在文件顶层作用域的任何位置。
后面的 import 也是一样。
import
- 基本用法
使用 export 定义了模块的对外接口以后,其它 JS 文件就可以通过 import 加载这个模块了:
1 | // main.js |
- 重命名 export 定义的名称
import 也可以像 export 一样使用 as 关键字:
1 | import { lastName as surname } from './profile.js'; |
- import 输入的变量都是只读的
1 | import {a} from './xxx.js' |
但如果 a 是一个对象,修改 a 的属性是可以的:
1 | import {a} from './xxx.js' |
- 通过 import 执行所加载的模块
1 | import 'lodash'; |
上面的代码仅仅执行了 loadash 模块,但你不输入任何值。
多次重复执行同一个 import 语句只会执行一次:
1 | import 'lodash'; |
下面的写法:
1 | import { foo } from 'my_module'; |
模块的整体加载
可以使用 * 指定一个对象,将模块的所有输出值都加载在这个对象上面:
1 | // circle.js |
整体加载用法:
1 | import * as circle from './circle'; |
但整体对象的属性是不能修改的,所以下面的写法都会报错:
1 | import * as circle from './circle'; |
export default
- 基本用法
前面的例子可以看出,使用 import 命令的时候,用户需要知道索要加载的变量名和函数名。而 export default 命令可以为模块指定默认输出:
1 | // export-default.js |
使用的时候可以使用 import 命令为匿名函数指定任意名字:
1 | // import-default.js |
- export default 输出的变量 import 时不需要用大括号
比如:
1 | // 第一组 |
一个文件只能有一个 export default
export default 的本质
export default 其实就是输出一个叫做 default 的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。
1 | // modules.js |
模块的继承
假设有一个 circleplus 模块,继承 circle 模块,可以这么写:
1 | // circleplus.js |
这样外面使用 circleplus 的时候就可以直接使用 circle 开放的内容。