ECMAScript 6 之Module
1. 概述
ES6 之前,模块加载方案主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。
ES6 在语言标准的层面上,实现了模块功能,ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
2. 严格模式
ES6中模块自动采用严格模式,关于严格模式,请参考:严格模式。
3. export 命令
模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量。下面是一个例子:
// filter.js
// 将0、1 类型 week 转成汉字,如 1001101 转成 “日,三,四,六”
export function weekFormat(str) {
if (typeof str !== 'string' || str === '') return ''
const week = str.trim().split('')
return ['日', '一', '二', '三', '四', '五', '六']
.filter((item, index) => {
return week[index] === '1'
})
.join(',')
}
上面代码是filter.js
文件的内容,定义了一个星期转换函数weekFormat
。ES6中,该文件就是一个模块,使用export
命令对外输出了函数weekFormat
。
export
命令还有一种写法,如下:
// filter.js
// 将0、1 类型 week 转成汉字,如 1001101 转成 “日,三,四,六”
function weekFormat(str) {
if (typeof str !== 'string' || str === '') return ''
const week = str.trim().split('')
return ['日', '一', '二', '三', '四', '五', '六']
.filter((item, index) => {
return week[index] === '1'
})
.join(',')
}
export { weekFormat }
上面代码在export
命令后面,使用大括号({}
)指定所要输出的一组变量。这种写法与上面的写法是等价的,但是更加清晰,一目了然。
通常情况下,export
输出的变量就是本来的名字,但是可以使用as
关键字重命名。
function f1() {
...
}
export { f1 as getName}
export
命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
export 1; // 报错
var m = 1;
export m; // 报错
正确写法如下:
export var m = 1;
// 或
var n = 1;
export { n }
注意:
-
export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。 -
export
命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错。
4. import 命令
使用export
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这个模块。
// another.js
// 加载模块
import { weekFormat } from './filter.js'
// 使用模块
var util = {
weekFormat
}
import
命令接受一对大括号({}
),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块对外接口的名称相同。
import
命令也支持使用as
关键字,将输入的变量重命名。
import { weekFormat as a } from './filter.js'
import
命令输入的变量都是只读的,因为它的本质是输入接口。即不允许在加载模块的脚本里面,改写接口。
import { a } from './xxx.js'
a = {} // Syntax Error : 'a' is read-only;
如果a
是一个对象,改写a
的属性是允许的。
import { a } from './xxx.js'
a.propertyName = ...
上面改写对象a
的属性是合法的。
import
命令具有提升效果,会提升到整个模块的头部,首先执行。
// 使用模块
var a = {
weekFormat
}
// 加载模块
import { weekFormat } from './filter.js'
上面例子中,先试用要加载的模块,再通过import
命令加载要使用的模块,是可以的,因为import
命令具有提升效果。
import
命令是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
// 报错
import { 'week' + 'Format' } from './filter.js'
import
后面的from
指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js
后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。
5. export default 命令
export default
命令,为模块指定默认输出。
// a.js
export default function (x, y) {
return x + y
}
上面例子是一个模块文件a.js
,通过export default
命令指定了它的默认输出是一个函数。
其他模块加载该模块时,import
命令可以为该匿名函数指定任意名字。
import add from './a.js'
上面例子的import
命令,可以用任意名称指向a.js
输出的方法,但是,此时import
命令后面,不使用大括号。
export default
命令也可以用在非匿名函数上。
// b.js
export default function add(x, y) {
return x + y;
}
//或者
function add(x, y) {
return x + y;
}
export default add;
上面例子中,函数add
的函数名add
只在模块内有效,在模块外部是无效的,加载的时候视同匿名函数。
下面是export
与export default
命令的简单比较。
// export default 命令
export default function sum() {...} // 输出
import sum from 'sum' // 输入
// export 命令
export function sum() {...} // 输出
import { sum } from 'sum' // 输入
本质上,export default
命令就是输出一个叫做default
的变量或方法,下面写法是有效的。
function add(x, y) {
return x * y;
}
export {add as default};
// 等价于
export default add;
export default
命令后面不能跟变量声明语句。
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
上面例子中,最后一种写法不允许,因为export default a
的含义是将变量a
的值赋给变量default
。
export default
命令的本质是将后面的值,赋给default
变量,下面写法是正确的。
// 错误
export 12;
// 正确
export default 12;
6. export 与 import 的复合写法
如果在一个模块之中,先输入后输出同一个模块,import
语句可以与export
语句写在一起。
export { foo, bar } from 'my_module';
// 等价于
import { foo, bar } from 'my_module';
export { foo, bar };
模块的接口改名可以采用复合写法。
export { foo as my_foo, bar as my_bar } from 'my_module';
默认接口可以采用复合写法。
export { default } from 'my_module'
具名接口改为默认接口的写法如下。
export { foo as default } from 'my_module'
// 等价于
import { foo } from 'my_module'
export default foo;
默认接口改为具名接口写法如下:
export { default as foo } from 'my_module'
下一篇: event demo