16.3 ES6 模块初窥

有两种导出的方式:命名导出(每个模块可以导出若干个)和默认导出(每个模块只能导出一个)。

16.3.1 命名导出(每个模块可以导出若干个)

通过在声明的前面加上 export 关键字,一个模块可以导出多个内容。这些导出的内容通过名字区分,被称为命名导出。

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

还有其它方式指定命名导出(后面讲解),但是我发现这种方式非常方便:尽情书写代码,就像没有外部的世界一样,然后给想要导出的所有内容加上一个关键字标签。

也可以引入整个模块,然后通过对象属性的方式来访问命名导出的内容:

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

在 CommonJS 中有相似的语法: 有段时间,在 Node.js 中,我尝试几种看起来很聪明的方法去处理模块导出,以便少写一些多余的代码。现在我倾向于下面这种简单的但是稍微啰嗦的风格,这不禁让人联想到文章《 revealing module pattern 》

//------ lib.js ------
var sqrt = Math.sqrt;
function square(x) {
    return x * x;
}
function diag(x, y) {
    return sqrt(square(x) + square(y));
}
module.exports = {
    sqrt: sqrt,
    square: square,
    diag: diag,
};

//------ main.js ------
var square = require('lib').square;
var diag = require('lib').diag;
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

16.3.2 默认导出(每个模块只能导出一个)

仅导出单一值的模块在 Node.js 社区中非常流行。但在前端开发中也很常见,经常为 model 写一些构造器或者类,每个模块一个 model 。一个 ECMAScript 6 模块可以只有一个默认导出。默认导出的内容特别方便引入。

下面的 ECMAScript 6 模块“是”一个单一的函数:

//------ myFunc.js ------
export default function () { ··· } // no semicolon!

//------ main1.js ------
import myFunc from 'myFunc';
myFunc();

默认值是类的 ECMAScript 6 模块看起来像下面这样:

//------ MyClass.js ------
export default class { ··· } // no semicolon!

//------ main2.js ------
import MyClass from 'MyClass';
let inst = new MyClass();

16.3.2.1 export default 的操作数:声明或表达式

在语法上, export default 的操作数是:

  • 一个声明:如果以函数或类开始。这意味着在前面的代码示例中,是匿名声明(仅能用于此种特殊情形)。
  • 一个表达式:所有其余场景。

因此,有两种可以默认导出匿名函数的方法:

export default function () {} // function declaration
export default (function () {}); // function expression

也可以给函数声明一个名字。如果想在模块的其它位置访问默认导出的内容,这会很有用:

bar();

export default function bar() { ··· }

这段代码等价于:

bar();

function bar() { ··· }

export default bar;

16.3.3 模块导出的是不变的绑定,而不是值

相对于 AMD 模块和 CommonJS 模块, ES6 模块并不导出值(值的快照),而是绑定(指向值存储位置的引用)。这些绑定是不可变的:只能通过绑定读取值,不能修改值。但是可以从模块内部修改。下面的代码说明了这是如何运作的:

//------ lib.js ------
export let mutableValue = 3;
export let incMutableValue() {
    mutableValue++;
}

//------ main1.js ------
import { mutableValue, incMutableValue } from './lib';

// The imported value is live
console.log(mutableValue); // 3
incMutableValue();
console.log(mutableValue); // 4

// The imported value can’t be changed
mutableValue++; // TypeError

如果通过星号(*)引入,会得到类似的结果:

//------ main2.js ------
import * as lib from './lib';

// The imported value is live
console.log(lib.mutableValue); // 3
lib.incMutableValue();
console.log(lib.mutableValue); // 4

// The imported value can’t be changed
lib.mutableValue++; // TypeError

16.3.4 引入的内容会提升

模块引入会提升(在内部处理中,会将引入移到当前作用域的最前面)。因此,在一个模块中,何处引入是没有关系的,下面的代码也并没有什么问题:

foo();

import { foo } from 'my_module';

16.3.5 模块文件就是普通的 JavaScript 文件

下面所有种类的文件都有相同的文件扩展名 .js

  • 1、 ECMAScript 6 模块
  • 2、 CommonJS 模块
  • 3、 AMD 模块
  • 4、 脚本文件(在 HTML 文件中通过<script src="file.js">加载)

也就是说,所列出的所有文件都是“ JavaScript 文件”。它们被如何解析,依赖于所在的使用环境。

results matching ""

    No results matching ""