一些资料
阮一峰的《ECMAScript 6 入门》中,有对模块机制的详细介绍:
http://es6.ruanyifeng.com/#docs/module
Babel 官方的《Learn ES2015》则相对比较简略:
https://babeljs.io/learn-es2015/#ecmascript-2015-features-modules
http://es6-features.org/ 官方的介绍很简洁:
http://es6-features.org/#ValueExportImport
严格加载模式
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict"; 严格模式主要有以下限制:
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用with语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀0表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
- eval不会在它的外层作用域引入变量
- eval和arguments不能被重新赋值
- arguments不会自动反映函数参数的变化
- 不能使用arguments.callee
- 不能使用arguments.caller
- 禁止this指向全局对象
- 不能使用fn.caller和fn.arguments获取函数调用的堆栈
- 增加了保留字(比如protected、static和interface)
export default 命令
下面两行代码的差别是,前者针对的是一个 export default 的输出,而后者则是一个 export someName 这样的输出,在使用 import 的时候,后者需要使用「{}」符号,前者不需要:
// 第一组
export default function () { // 输出
// ...
}
import crc32 from './crc32'; // 输入
// 第二组
export function crc32() { // 输出
// ...
};
import {crc32} from './crc32'; // 输入
如果想在一条import语句中,同时输入默认方法和其他变量,可以写成下面这样:
import lodash, { each } from 'lodash';
export 与 import 的复合写法
export { foo, bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
export { foo, bar };
// 整体输出
export * from 'my_module';
// 整体输出默认接口
export { default } from 'foo';
require()
require() is not part of your standard JavaScript. In context to your question and tags, require() is built into Node.js to load modules. The concept is similar to C/Java/Python/[insert more languages here] imports or includes.
浏览器上的不同加载方式
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
defer与async的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。
<script type="module" src="foo.js"></script>
上面代码在网页中插入一个模块foo.js,由于type属性设为module,所以浏览器知道这是一个 ES6 模块。
浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。
<script type="module" src="foo.js"></script>
<!-- 等同于 -->
<script type="module" src="foo.js" defer></script>
script 标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染。
<script type="module" src="foo.js" async></script>
ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。
<script type="module">
import utils from "./utils.js";
// other code
</script>
对于外部的模块脚本(上例是foo.js),有几点需要注意。
- 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
- 模块脚本自动采用严格模式,不管有没有声明use strict。
- 模块之中,可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export命令输出对外接口。
- 模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字,是无意义的。
- 同一个模块如果加载多次,将只执行一次。