Skip to content

深入理解 ES6 #13-用模块封装代码

什么是模块

模块是自动运行在严格模式下并且没有办法退出运行的 JavaScript 代码。

在模块顶部创建的变量不会自动被添加到全局变量作用域,这个变量仅在模块的顶级作用域中存在,而且模块必须导出一些外部代码可以访问的元素。

模块也可以从其他模块导入绑定。

在模块的顶部,this 的值是 undefined

模块不支持 HTML 风格的代码注释。

导出的基本语法

export 关键字将一部分已发布的代码暴露给其它模块。

js
// 导出数据
export var color = "red";
export let name = "JiaJia";
export const magicNumber = 7

// 导出函数
export function sum(num1, num2) {
    return num1 + num2;
}

// 导出类
export class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
}

// this is private function
function subtract(num1, num2) {
    return num1 - num2;
}

// define a function
function multiply(num1, num2) {
    return num1 * num2;
}

// export it
export multiply;

导入的基本语法

通过 import 关键字导入在另一个模块中导出的功能。

js
import { identifier1, identifier2 } from "./example.js"

import 后面的大括号表示从给定模块导入的绑定(binding),关键字 from 表示从哪个模块导入给定的绑定。

当从模块中导入一个绑定时,它就好像使用 const 定义的一样。结果是你无法定义另一个同名变量(包括导入另一个同名绑定),也无法在 import 语句前使用标识符或改变绑定的值。

导入单个绑定

js
import { sum } from "./example.js";

console.log(sum(1, 2)); // 3

// 抛出错误
sum = 1;

导入多个绑定

js
import { sum, multiply, magicNumber } from "./example.js"

console.log(sum(1, magicNumber)); // 8
console.log(multiply(1, 2)); // 2

导入整个模块

js
// 命名空间导入
import * as example from "./example.js";

console.log(example.sum(1, example.magicNumber)); // 8
console.log(example.multiply(1, 2)); // 2

不管在 import 语句中把一个模块导入了多少次,该模块只执行一次。

如果同一个应用程序中的其他模块也从 example.js 导入绑定,那么这些模块与此代码将使用相同的模块实例。

导入绑定的一个微妙怪异之处

js
export var name = "JiaJia";
export function setName(newName) {
    name = newName;
}
js
import { name, setName } from "./example.js";

console.log(name); // "JiaJia"
setName("L");
console.log(name); // "L"

name = "JiaJia"; // 抛出错误

导出和导入时重命名

js
function sum(num1, num2) {
    return num1 + num2;
}

export {
    sum as add
};
js
import { add as sum } from "./example.js";

模块的默认值

模块的默认值指的是通过 default 关键字指定的单个变量、函数或类,只能为每个模块设置一个默认的导出值。

js
export default function (num1, num2) {
    return num1 + num2;
}

等同于

js
function sum (num1, num2) {
    return num1 +  num2;
}

export default sum;

等同于

js
function sum (num1, num2) {
    return num1 +  num2;
}

export { sum as default };

导入默认值

js
import sum from "./example.js";

console.log(sum(1, 2)); // 3

导入默认值和以外的值:

js
import sum, { color } from "./example.js";

或者

js
import { default as sum, color } from "./example.js";

重新导出一个绑定

js
import { sum } from "./example.js";
export { sum }

或者简写成如下形式:

js
export { sum } from "./example.js";

导出时也可以定义别名

js
export { sum as add } from "./example.js";

导出一切:

js
export * from "./example.js";

无绑定导入

js
// 没有 export 或 import 的模块代码
Array.prototype.pushAll = function(items) {
    if (!Array.isArray(items)) {
        throw new TypeError("参数必须是一个数组");
    }

    return this.push(...items);
}
js
import "./example.js"

加载模块

在 Web 浏览器中使用模块

  • <script> 元素中通过 src 属性指定一个加载代码的地址来加载 JavaScript 代码文件

  • 将 JavaScript 代码内嵌到没有 src 属性的<script>元素中

  • 通过 Web Worker 或 Service Worker 的方法加载并执行 JavaScript 代码文件

<Script> 中使用模块

html
<!-- 加载一个 JavaScript 模块文件 -->
<script type="model" src="module.js"></script>

<!-- 内联引入一个模块 -->
<script type="model">
import { sum } from "./example.js";
let result = sum(1, 2);
</script>

Web 浏览器中的模块加载顺序

模块按照它们出现在 HTML 文件中的顺序执行,也就是说,无论模块中包含的是内联代码还是指定 src 属性,第一个 <script type="module"> 总是在第二个之前执行。

Web 浏览器中的异步模块加载

当应用 async 属性时,脚本文件将在文件完全下载并解析后执行。

脚本在文档中的顺序不会影响脚本执行的顺序。

html
<!-- 无法保证这两个哪个先执行 -->
<script type="module" async src="module1.js"></script>
<script type="module" async src="module2.js"></script>

将模块作为 Worker 加载

Worker,例如 Web Worker 和 Service Worker,可以在网页上下文之外执行 JavaScript 代码。

js
// 默认按照脚本的方式加载 script.js
let worker = new Worker("script.js");
js
// 默认按照模块的方式加载 script.js
let worker = new Worker("script.js", { type: "module" });

浏览器模块说明符解析

浏览器要求模块说明符具有以下几种格式之一:

  • / 开头的解析为从根目录开始
  • . /开头的解析为从当前目录开始
  • ../ 开头的解析为从父目录开始
  • 以 URL 格式
js
// 从 https://www.example.com/modules/example1.js 导入
import { first } from "./example1.js";

// 从 https://www.example.com/example2.js 导入
import { second } from "../example2.js";

// 从 https://www.example.com/example3.js 导入
import { third } from "./example3.js";

// 从 https://www.example.com/example4.js 导入
import { fourth } from "https://www.example.com/example4.js";

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.