模块化:ES Modules规范(import/export) (ES6+)讲座
你好,模块化世界!
大家好,欢迎来到今天的讲座!今天我们要聊聊 JavaScript 的模块化系统——ES Modules。如果你还在用 var
和全局变量,那你就OUT了!ES Modules 是 ES6+ 引入的一项重要特性,它让我们的代码更加整洁、可维护,并且避免了命名冲突和全局污染。
什么是模块?
在编程中,模块 是一个独立的、可复用的代码单元。它可以包含函数、类、变量等,但最重要的是,模块可以导出(export)一些内容供其他模块使用,也可以导入(import)其他模块的内容。
在 ES6 之前,JavaScript 没有原生的模块系统,开发者通常依赖于第三方库(如 CommonJS、AMD 等)来实现模块化。但现在,ES Modules 已经成为了 JavaScript 的标准模块系统,支持直接在浏览器和 Node.js 中使用。
ES Modules 的基本语法
1. 导出(Export)
要让一个模块中的内容可以被其他模块使用,我们需要使用 export
关键字。ES Modules 提供了两种主要的导出方式:
- 默认导出(Default Export)
- 命名导出(Named Export)
默认导出
每个模块只能有一个默认导出。默认导出可以是任何东西:函数、类、对象、甚至是一个简单的值。默认导出的好处是,你在导入时可以给它起任意名字。
// math.js
export default function add(a, b) {
return a + b;
}
命名导出
命名导出允许你导出多个内容,并且每个导出都有一个明确的名字。你可以导出多个函数、类、变量等。
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;
你还可以一次性导出多个内容:
// math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
const PI = 3.14159;
export { add, subtract, PI };
2. 导入(Import)
要使用其他模块导出的内容,我们需要使用 import
关键字。根据导出的方式不同,导入的方式也有所不同。
导入默认导出
当你导入一个默认导出时,你可以给它起任意名字。注意,默认导出不需要用大括号包裹。
// main.js
import add from './math.js';
console.log(add(2, 3)); // 输出: 5
导入命名导出
当你导入命名导出时,必须使用大括号包裹,并且导入的名字必须与导出的名字一致。
// main.js
import { add, subtract, PI } from './math.js';
console.log(add(2, 3)); // 输出: 5
console.log(subtract(5, 2)); // 输出: 3
console.log(PI); // 输出: 3.14159
你还可以为命名导出指定别名:
// main.js
import { add as sum, subtract as minus, PI as pi } from './math.js';
console.log(sum(2, 3)); // 输出: 5
console.log(minus(5, 2)); // 输出: 3
console.log(pi); // 输出: 3.14159
全部导入
如果你想导入模块中的所有导出内容,可以使用 * as
语法。这会将所有导出的内容作为一个对象导入。
// main.js
import * as math from './math.js';
console.log(math.add(2, 3)); // 输出: 5
console.log(math.subtract(5, 2)); // 输出: 3
console.log(math.PI); // 输出: 3.14159
ES Modules 的特点
ES Modules 与其他模块系统相比,有一些独特的特点:
-
静态分析:ES Modules 是静态的,这意味着你可以在编译时解析模块的依赖关系。不像 CommonJS,它是动态的,依赖关系是在运行时解析的。静态分析使得工具(如打包工具、IDE)可以更好地优化和提示代码。
-
单例模式:每个模块只会被加载一次,并且它的导出内容是只读的。这意味着如果你在多个地方导入同一个模块,它们会共享同一个实例。
-
异步加载:在浏览器中,ES Modules 是异步加载的。你可以通过
<script type="module">
标签来加载模块,浏览器会自动处理模块的依赖关系并按顺序执行。 -
顶级作用域:在 ES Modules 中,所有的代码都处于模块的作用域内,不会污染全局作用域。这意味着你不再需要担心变量或函数会意外地覆盖全局变量。
实战演练:构建一个简单的模块化应用
让我们通过一个简单的例子来巩固一下所学的知识。假设我们要构建一个计算器应用,它有加法、减法、乘法和除法功能。
1. 创建 math.js
模块
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export function multiply(a, b) {
return a * b;
}
export function divide(a, b) {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
}
2. 创建 main.js
模块
// main.js
import { add, subtract, multiply, divide } from './math.js';
console.log('Addition:', add(2, 3)); // 输出: Addition: 5
console.log('Subtraction:', subtract(5, 2)); // 输出: Subtraction: 3
console.log('Multiplication:', multiply(4, 5)); // 输出: Multiplication: 20
console.log('Division:', divide(10, 2)); // 输出: Division: 5
3. 在 HTML 中引入模块
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Calculator</title>
</head>
<body>
<h1>Simple Calculator</h1>
<script type="module" src="./main.js"></script>
</body>
</html>
ES Modules 与 CommonJS 的区别
特性 | ES Modules | CommonJS |
---|---|---|
加载方式 | 静态加载(编译时解析) | 动态加载(运行时解析) |
单例模式 | 是 | 是 |
作用域 | 模块作用域(不污染全局) | 全局作用域(require 返回的对象) |
异步加载 | 支持(浏览器中) | 不支持(同步加载) |
导出方式 | 默认导出、命名导出 | 只有命名导出(module.exports ) |
使用场景 | 浏览器、Node.js(ESM 模式) | Node.js(CJS 模式) |
结语
好了,今天的讲座就到这里啦!通过 ES Modules,我们可以轻松地将代码拆分为多个模块,提升代码的可维护性和复用性。希望你能把今天学到的知识应用到实际项目中,写出更优雅的 JavaScript 代码!
如果你有任何问题,或者想了解更多关于 ES Modules 的高级用法,欢迎随时提问!下次见! 😊
参考文献
- MDN Web Docs: ES Modules
- ECMAScript Specification: Modules
- V8 Engine Documentation: ES Modules Support