ES6 模块 vs JS 普通文件
对比项 | ES6 模块 | JS 普通文件 |
---|---|---|
顶级变量 | 模块范围 | 全局范围 |
默认模式 | 严格模式(Strict Mode) | 非严格模式(No-Strict Mode,Sloppy Mode) |
顶级 this 值 | undefined | window |
导入/导出 | 支持,但只支持顶级导入/导出,且导入会 Hoisted | 不支持 |
HTML 中 | <script type="module"> | <script> |
下载文件 | 异步 | 同步 |
ES6 模块特点
- 一个文件代表一个模块
- 模块通过同步导入,但模块文件通过异步下载
- 导出/导入是 Live Binding,即引用(或理解为指针)
导入/导出模块
导入语法:
JavaScript
1// 静态导入
2import defaultExport from 'module-name';
3import * as name from 'module-name';
4import { export1, export2 as alias2, ... } from 'module-name';
5import defaultExport, { export1, ... } from 'module-name';
6import defaultExport, * as name from 'module-name';
7import 'module-name';
8
9// 动态导入
10const promise = import('module-name');
11const module = await import('module-name');
导出语法:
JavaScript
1// 命名导出(一个模块可以有0个或多个)
2// 分别导出
3export let name1, ..., nameN; // 同 `var`,`const`
4export let name1 = value1, ..., nameN = valueN; // 同 `var`,`const`
5export function functionName() {...}
6export class ClassName {...}
7
8// 批量导出
9export { name1, ..., nameN };
10
11// 重命名导出
12export { variable1 as name1, ..., nameN };
13
14// 重命名解构赋值导出
15export const { name1, name2: bar } = o;
16
17// 默认导出(一个模块仅有一个)
18export default expression;
19export default function (...) { ... } // 同 `class`,function*
20export default function name1(...) { ... } // 同 `class`,function*
21export { name1 as default, ... };
示例,index.js
JavaScript
1import {
2 lan, // 导入命名导出
3 programs as p // 导入命名导出,并重命名引用
4} from './m1.js';
5
6console.log(lan);
7console.log(p);
8
9// 导入模块中的一切
10import * as m1 from './m1.js';
11
12console.log(m1.lan);
13console.log(m1.programs);
14
15// 导入默认导出
16import info from './m1.js';
17
18info();
19
20// 混合导入(不推荐,命名导出和默认导出应该是二选其一)
21import log, { programs } from './m1.js';
22
23log();
24console.log(programs);
25
26// 验证 Live Binding
27import { arr, add } from './m1.js';
28
29add(1);
30add(2);
31console.log(arr); // [1, 2]
32
33// 动态导入
34import('./m1.js').then((module) => {
35 module.default();
36});
37
38// 动态导入 `await`
39const module = await import('./m1.js');
40console.log(module.arr);
m1.js
JavaScript
1// 命名导出
2export const programs = ['JS', 'Java'];
3
4// 重命名后导出
5const languages = ['zh', 'en'];
6
7export { languages as lan };
8
9// 默认导出,一个模块仅存在一个默认导出
10export default () => {
11 console.log('默认模块');
12};
13
14// 验证 Live Binding
15export const arr = [];
16
17export const add = (val) => {
18 arr.push(val);
19};
index.html
HTML
1<script type="module" src="index.js"></script>
ES6 以前的模块模式
在 ES6 模块引入之前,我们常用的一种编码模式是通过 IIFE(Immediately Invoked Function Expression)封装代码块,从而区分私有和公开。
JavaScript
1var api = (function () {
2 var privateVal = '私有变量';
3
4 var publicFunc = function () {
5 console.log('需暴露出的公有 API');
6 }
7
8 return {
9 publicFunc
10 };
11})();
12
13api.publicFunc();
14
15console.log(api.privateVal); // undefined
缺点:
- 无法避免变量名的全局污染
- 需手动识别 JS 文件间的依赖关系,在 HTML 中必须是被依赖的 JS 先导入