myfreax

JavaScript ES6 模块

在本教程中,您将了解 ES6 模块 以及如何从模块中导出变量、函数和类,并在其他模块中使用它们

JavaScript ES6 模块
JavaScript ES6 模块

在本教程中,您将了解 ES6 模块 以及如何从模块中导出变量、函数和类,并在其他模块中使用它们。

ES6 模块是一个仅在严格模式执行的 JavaScript 文件。这意味着模块中声明的任何变量函数都不会自动添加到全局作用域。

在 Web 浏览器运行模块

首先,创建一个 message.js 文件并添加以下代码:

export let message = 'ES6 Modules';

message.js 是包含 message 变量的 ES6 模块。export 语句将 message 变量公开给其他模块。

其次,创建另一个新文件 app.js 使用 message.js 模块。app.js 模块创建一个新的 h1 元素并将追加到 HTML 页面。import 语句从 message.js 模块导入 message 变量。

import { message } from './message.js'

const h1 = document.createElement('h1');
h1.textContent = message

document.body.appendChild(h1)

最后,创建一个 HTML 页面使用 app.js 模块:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>ES6 Modules</title>
</head>
<body>
<script type="module" src="./app.js"></script>
</body>
</html>

请注意,我们在 <script> 标签中使用  type="module" 来加载 app.js 模块。如果您在 Web 浏览器查看该页面,您将看到以下页面:

下面,让我们更详细地理解导出 export 和导入 import 语句。

export

要导出变量函数,请将 export 关键字放在其前面,如下所示:

// log.js
export let message = 'Hi';

export function getMessage() {
  return message;
}

export function setMessage(msg) {
  message = msg;
}

export class Logger {
}

在这个例子中,我们有一个带有变量、两个函数和一个类的模块 log.js 。我们使用 export 关键字导出模块的所有。

请注意,export 关键字要求函数或类具有要导出的名称。您不能使用此语法导出匿名函数或类。

JavaScript 允许您先定义变量、函数或类,然后再将其导出,如下所示:

// foo.js
function foo() {
   console.log('foo');
}

function bar() {
  console.log('bar');
}
export foo;

在这个例子中,我们先定义 foo() 函数,然后导出它。由于我们没有导出 bar() 函数,因此我们无法在其他模块中访问 bar() 函数。bar() 函数在模块外是不可访问的,或者我们说它是私有的。

导入

一旦你定义一个带有导出的模块,你就可以使用 import 关键字访问另一个模块导出的变量、函数和类。下面是 import 语法形式:

import { what, ever } from './other_module.js';

在这个语法中:

  • 首先,在花括号内指定要导入的内容,这称为绑定。
  • 然后,从中指定的模块导入绑定。
请注意,当您从模块导入绑定时,绑定的行为就像使用 const 定义的一样。这意味着您不能拥有另一个具有相同名称的标识符或更改绑定的值。

请阅读以下示例:

// greeting.js
export let message = 'Hi';

export function setMessage(msg) {
  message = msg;
}

当你导入 message 变量和 setMessage() 函数时,你可以使用 setMessage() 函数来改变 message 变量的值,如下所示:

// app.js
import {message, setMessage } from './greeting.js';
console.log(message); // 'Hi'

setMessage('Hello');
console.log(message); // 'Hello' 

但是,您不能直接更改  message 变量的值。以下表达式会抛出错误:

message = 'Hallo'; // error

在背后,当您调用 setMessage() 函数时。JavaScript 返回 greeting.js 模块并执行其中的代码并更改 message 变量。然后更改会自动反映在导入的 message 绑定。

app.jsmessage 绑定是 message 变量导出的本地名称,所以 message 变量在 app.jsgreeting.js 模块是不一样的。

导入单个绑定

假设您有一个包含以下变量的 foo 模块:

// foo.js
export foo = 10;

然后,在另一个模块中,您可以重用 foo 变量:

// app.js
import { foo } from './foo.js';
console.log(foo); // 10;

但是,您不能更改 foo 的值。如果您尝试这样做,JavaScript 引擎将会抛出错误消息:

foo = 20; // throws an error

导入多个绑定

假设您有如下  cal.js  模块:

// cal.js
export let a = 10,
           b = 20,
           result = 0;

export function sum() {
  result = a + b;
  return result;
}

export function multiply() {
  result = a * b;
  return result;
}

而你想从 cal.js 导入这些绑定,你可以显式列出它们,如下所示:

import {a, b, result, sum, multiply } from './cal.js';
sum();
console.log(result); // 30

multiply();
console.log(result); // 200

将整个模块作为对象导入

要将模块中的所有内容作为单个对象导入,请使用星号 * ,如下所示:

import * as cal from './cal.js'; 

在此示例中,我们将 cal.js 模块中的所有绑定作为 cal 对象导入。在这种情况下,所有绑定都成为 cal 对象的属性,因此您可以按以下方式访问它们:

cal.a;
cal.b;
cal.sum();

这种导入方式称为命名空间导入

重要的是要记住,即使导入多次,导入的模块也只会执行一次请阅读下面的例子:

import { a } from './cal.js';
import { b } from './cal.js';
import {result} from './cal.js';

在第一条 import 语句之后,cal.js 模块被执行并加载到内存,每当被后续 import 语句引用时,它就会被重用。

import 和 export 声明的限制

请注意,您必须在其他语句和函数的外部使用 import 或者 export 语句,否则会导致语法错误。

下面的示例会导致 SyntaxError

if( requiredSum ) {
   export sum;
}  

因为我们使用 export 语句在 if 语句里面。同样, 下面的 import 语句也会导致SyntaxError

function importSum() {
   import {sum} from './cal.js';
}

因为我们在函数内部使用 import 语句。错误的原因是 JavaScript 必须确定将导出和导入的内容。

请注意,ES2020 引入类函数对象 import(),它允许您动态导入模块。

导入与导出的别名 as关键词

JavaScript 允许您在导出和导入时变量、函数或类时创建别名。请阅读以下 math.js模块:

// math.js  
function add( a, b ) {
   return a + b;
}

export { add as sum };

在此示例中,我们没有导出函数 add() ,而是使用 as 关键字为 add() 函数分配给别名 sum

因此,当您从 math.js 模块导入函数时,您必须改用 sum

import { sum } from './math.js';

如果你想在导入时使用不同的名称,你可以使用 as 关键字如下:

import {sum as total} from './math.js';

重新导出

可以导出已导入的绑定。这称为重新导出。例如:

import { sum } from './math.js';
export { sum };

在这个例子中,我们从 math.js 模块导入 sum 并重新导出它。下面的语句与上面的语句等价:

export {sum} from './math.js';

如果您想在重新导出之前重命名绑定,请使用关键字 as。以下示例从 math.js 模块导入 sum 并将其重新导出为 add

export { sum as add } from './math.js';

如果要从另一个模块导出所有绑定,可以使用星号 * :

export * from './cal.js';

无绑定导入

有时,您希望开发一个不导出任何内容的模块,例如,您可能希望向 Array 等内置对象添加方法。

// array.js
if (!Array.prototype.contain) {
  Array.prototype.contain = function(e) {
    // contain implementation
    // ...
  }
}

现在,您可以在没有任何绑定的情况下导入模块,并使用 array.js 模块中定义的  contain() 方法,如下所示:

import './array.js';
[1,2,3].contain(2); // true

默认导出

一个模块只能有一个默认导出。默认导出更容易导入。模块的默认值可以是变量、函数或类。

以下是带有默认导出的 sort.js 模块。

// sort.js
export default function(arr) {
  // sorting here
} 

请注意,您不需要指定函数的名称,因为模块代表函数名称。

import sort from sort.js;
sort([2,1,3]);

如您所见,标识符 sort 代表 sort.js 模块的默认函数。请注意,我们没有在标识符 sort 周围使用花括号 {}

让我们更改 sort.js 模块包含默认导出和非默认导出:

// sort.js
export default function(arr) {
  // sorting here
}
export function heapSort(arr) {
  // heapsort
}

要导入默认和非默认绑定,您可以在 import 关键字后使用以下规则指定绑定列表:

  • 默认绑定必须首先出现。
  • 非默认绑定必须用花括号括起来。

请阅读以下示例:

import sort, {heapSort} from './sort.js';
sort([2,1,3]);
heapSort([3,1,2]);

要重命名默认导出,您还可以使用 as 关键字,如下所示:

import { default as quicksort, heapSort} from './sort.js';

结论

在本教程中,您了解 ES6 模块以及如何从一个模块导出绑定并将它们导入另一个模块。

内容导航