多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### 1. ES6 模块不是对象,而是通过`export`命令显式指定输出的代码,再通过`import`命令输入。 ~~~javascript // ES6模块 import { stat, exists, readFile } from 'fs'; ~~~ 上面代码的实质是从`fs`模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。 ### 2. 严格模式 * 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`) ### 3.export 命令 * 模块功能主要由两个命令构成:`export`和`import`。`export`命令用于规定模块的对外接口,`import`命令用于输入其他模块提供的功能。 `export`的写法,除了像上面这样,还有另外一种。 ~~~javascript // profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export { firstName, lastName, year }; ~~~ 上面代码在`export`命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在`var`语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。 `export`命令除了输出变量,还可以输出函数或类(class)。 ~~~javascript export function multiply(x, y) { return x * y; }; ~~~ 上面代码使用`as`关键字,重命名了函数`v1`和`v2`的对外接口。重命名后,`v2`可以用不同的名字输出两次。 上面代码对外输出一个函数`multiply`。 通常情况下,`export`输出的变量就是本来的名字,但是可以使用`as`关键字重命名。 ~~~javascript function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion }; ~~~ ### 3.import命令 * 使用`export`命令定义了模块的对外接口以后,其他 JS 文件就可以通过`import`命令加载这个模块。 ~~~javascript // main.js import { firstName, lastName, year } from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; } ~~~ * 上面代码的`import`命令,用于加载`profile.js`文件,并从中输入变量。`import`命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(`profile.js`)对外接口的名称相同。 如果想为输入的变量重新取一个名字,`import`命令要使用`as`关键字,将输入的变量重命名。 ~~~javascript import { lastName as surname } from './profile.js'; ~~~ * `import`命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。 * `import`后面的`from`指定模块文件的位置,可以是相对路径,也可以是绝对路径,`.js`后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。 ~~~javascript import {myMethod} from 'util'; ~~~ 上面代码中,`util`是模块文件名,由于不带有路径,必须通过配置,告诉引擎怎么取到这个模块 ### 5.整体加载 除了指定加载某个输出值,还可以使用整体加载,即用星号(`*`)指定一个对象,所有输出值都加载在这个对象上面。 下面是一个`circle.js`文件,它输出两个方法`area`和`circumference`。 ~~~javascript // circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; } ~~~ 现在,加载这个模块。 ~~~javascript // main.js import { area, circumference } from './circle'; console.log('圆面积:' + area(4)); console.log('圆周长:' + circumference(14)); ~~~ 上面写法是逐一指定要加载的方法,整体加载的写法如下。 ~~~javascript import * as circle from './circle'; console.log('圆面积:' + circle.area(4)); console.log('圆周长:' + circle.circumference(14)); ~~~ ### 6.export default命令 为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到`export default`命令,为模块指定默认输出。 ~~~javascript // export-default.js export default function () { console.log('foo'); } ~~~ 上面代码是一个模块文件`export-default.js`,它的默认输出是一个函数。 其他模块加载该模块时,`import`命令可以为该匿名函数指定任意名字。 ~~~javascript // import-default.js import customName from './export-default'; customName(); // 'foo' ~~~ 上面代码的`import`命令,可以用任意名称指向`export-default.js`输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时`import`命令后面,不使用大括号。 `export default`命令用在非匿名函数前,也是可以的。 ~~~javascript // export-default.js export default function foo() { console.log('foo'); } // 或者写成 function foo() { console.log('foo'); } export default foo; ~~~ 上面代码中,`foo`函数的函数名`foo`,在模块外部是无效的。加载的时候,视同匿名函数加载。 下面比较一下默认输出和正常输出。 ~~~javascript // 第一组 export default function crc32() { // 输出 // ... } import crc32 from 'crc32'; // 输入 // 第二组 export function crc32() { // 输出 // ... }; import {crc32} from 'crc32'; // 输入 ~~~ 上面代码的两组写法,第一组是使用`export default`时,对应的`import`语句不需要使用大括号;第二组是不使用`export default`时,对应的`import`语句需要使用大括号。 `export default`命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此`export default`命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应`export default`命令。 ### 7.import()方法 因为`require`是运行时加载模块,`import`命令无法取代`require`的动态加载功能。 ~~~javascript const path = './' + fileName; const myModual = require(path); ~~~ 上面的语句就是动态加载,`require`到底加载哪一个模块,只有运行时才知道。`import`命令做不到这一点。 因此,有一个[提案](https://github.com/tc39/proposal-dynamic-import),建议引入`import()`函数,完成动态加载。 ~~~javascript import(specifier) ~~~ 上面代码中,`import`函数的参数`specifier`,指定所要加载的模块的位置。`import`命令能够接受什么参数,`import()`函数就能接受什么参数,两者区别主要是后者为动态加载。 `import()`返回一个 Promise 对象。下面是一个例子。 ~~~javascript const main = document.querySelector('main'); import(`./section-modules/${someVariable}.js`) .then(module => { module.loadPageInto(main); }) .catch(err => { main.textContent = err.message; }); ~~~ `import()`函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,`import()`函数与所加载的模块没有静态连接关系,这点也是与`import`语句不相同。`import()`类似于 Node 的`require`方法,区别主要是前者是异步加载,后者是同步加载。