在ECMAScript 5及早先版本中,一直没有模块体系,使得无法将一个复杂的应用拆分成不同的功能模块,再组合起来使用。因此,JavaScript社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。
而在ECMAScript 6中,实现了模块功能,以更简单的方式取代CommonJS和AMD规范,现在已经成为浏览器和服务器通用的模块解决方案。
使用export关键字直接导出模块
// 文件 modules.js
// 导出数据
export var color = "red"
export let name = "module"
export const sizeOfPage = 10
// 导出函数
export function sum(a, b) {
return a + b
}
// 以下内容将在模块末尾进行导出
function subtract(a, b) {
return a - b
}
function multiply(a, b) {
return a * b
}
function divide(a, b) {
return a / b
}
// 导出类
export class Car {
constructor(color, doors) {
this.color = color
this.doors = doors
}
showColor() {
console.log(this.color)
}
}
// 模块私有变量
var count = 0
// 模块私有函数
function changeCount() {
count++
}
// 导出multiply函数
export {multiply}
// subtract是本地名称,sub是导出时使用的名称
export {subtract as sub}
// 导出模块的默认值
export default divide
需要说明的是:
- 导出时可以分别对变量、函数和类进行导出,也可以将导出语句集中写在模块的尾部,当导出的内容较多时,采用后者会更加清晰。
- 没有添加export关键字而定义的变量、函数和类在模块外部是不允许被访问的。
- 导出的函数和类声明都需要一个名称,如上述代码所示。如果要采用一个不同的名称导出变量、函数或者类,可以使用as关键字来指定变量、函数或者类在模块外应该按照什么样的名字来使用。
- 一个模块可以导出且只能导出一个默认值,默认值是通过使用default关键字指定的单个变量、函数、或者类。非默认值的导出,需要使用一对花括号包裹名称,而默认值的导出则不需要
- 默认值的导出还可以采用下面两种语法形式。
代码示例如下
// 使用default关键字导出一个函数作为模块的默认值
// 因为导出的函数被模块所代表,所以它不需要一个名称
export default function(a, b) {
if (b !== 0) {
return a / b
}
}
function divide(a, b) {
if (b !== 0) {
return a / b
}
}
export {divide as default}
如果想在一条导出语句中指定多个导出(包括默认导出),那么就需要用到第三种语法形式。
将modules.js中模块尾部的导出合并
export {
multiply,
subtract as sub,
divide as default
}
下面再来看一下导入。导入是通过使用import关键字来引入其他模块导出的功能。import语句由两个部分组成:
- 要导入的标识符
- 标识符所在模块
代码示例如下
// 文件 index.js
// 导入模块默认值
import divide from "./modules.js"
// 导入多个绑定
import {color, name, sizeOfPage} from "./modules.js"
// 导入单个绑定
import {multiply} from "./modules.js"
// 因modules模块中导出subtract函数时使用了名称sub,这里导入也要使用该名称
import {sub} from "./modules.js"
// 导入时重命名导入的函数
import {sum as add} from "./modules.js"
// 导入类
import {Car} from "./modules.js"
// 导入整个模块
import * as example from "./modules.js"
// red
console.log(color)
// module
console.log(name)
// 10
console.log(sizeOfPage)
// 只能用add而不能用sum
console.log(add(6, 2))
console.log(sub(6, 2))
console.log(multiply(6, 2))
console.log(divide(6, 2))
let car = new Car("black", 4)
// black
car.showColor()
// module
console.log(example.name)
// 注意这里是sum,而不是add
console.log(example.sum(6, 2))
// count是modules模块私有的变量,在外部不能访问
// 输出 undefined
console.log(example.count)
// changeCount()函数是modules模块的私有函数,在外部不能访问
// TypeError: example.changeCount is not a function
console.log(example.changeCount())
需要说明的是:
- 导入模块时,模块文件的位置可以使用相对路径,也可以使用绝对路径。使用相对路径时,对于同一目录下的文件,不能使用modules.js来引入,而要使用./modules.js,即通过"."来表示当前目录。
- 导入时,可以导入单个绑定,也可以同时导入多个绑定。导入时也可以使用as关键字对导入的绑定重新命名。
- 对于模块非默认值的导入,需要使用一对花括号包裹名称,而默认值的导入则不需要。
- 可以导入整个模块作为一个单一对象,然后所有的导出将作为该对象的属性使用。
- 多个import语句引用同一个模块,该模块也只执行一次。被导入的模块代码执行后,实例化后的模块被保存在内存中,只要另一个import语句引用它就可以重复使用它。
- 另: export和import语句必须在其他语句或者函数之外使用,换句话说,import和export语句只能在模块的顶层使用