10JavaScript高级程序设计(第4版)

模块

代码组织

ES6模块CommonJS导入导出

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第10章:模块(Modules)

一、导读

1.1 本章学习目标

第10章介绍JavaScript的模块化开发。本章核心目标:

  • 掌握ES6模块:import和export
  • 理解CommonJS模块:require和module.exports
  • 学会动态导入:import()
  • 了解模块打包工具:Webpack、Rollup、Vite

1.2 为什么本章如此重要

模块化是组织大型代码的关键。ES6模块是JavaScript的官方标准,提供了静态分析、Tree Shaking等优势。理解模块系统是现代JavaScript开发的基础。

1.3 核心问题引导

ES6模块和CommonJS有什么本质区别?

命名导出和默认导出有什么区别?

动态导入在什么场景下使用?

模块的循环依赖如何处理?


二、核心概念详解

10.1 ES6模块

javascript
// math.js - 导出
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export const PI = 3.14159;

export default class Calculator {
  constructor() {
    this.history = [];
  }
}

// main.js - 导入
import Calculator, { add, subtract, PI } from './math.js';

console.log(add(2, 3));      // 5
console.log(PI);             // 3.14159

// 重命名导入
import { add as plus } from './math.js';

// 导入所有
import * as math from './math.js';
console.log(math.add(2, 3));

10.2 CommonJS模块

javascript
// utils.js - CommonJS导出
function greet(name) {
  return `Hello, ${name}!`;
}

function farewell(name) {
  return `Goodbye, ${name}!`;
}

module.exports = { greet, farewell };

// 或者
// exports.greet = greet;
// exports.farewell = farewell;

// app.js - CommonJS导入
const { greet, farewell } = require('./utils');

console.log(greet("Alice"));
console.log(farewell("Bob"));

10.3 动态导入

javascript
// 按需加载
async function loadModule() {
  const module = await import('./math.js');
  console.log(module.add(2, 3));
}

// 条件导入
if (needsFeature) {
  import('./feature.js').then(module => {
    module.init();
  });
}

// 路由级别的代码分割
const routes = {
  '/dashboard': () => import('./Dashboard.js'),
  '/settings': () => import('./Settings.js'),
};

async function navigate(path) {
  const loader = routes[path];
  if (loader) {
    const module = await loader();
    module.render();
  }
}

10.4 模块最佳实践

javascript
// 1. 单一职责:每个模块只做一件事
// utils/validation.js
export function isValidEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

export function isValidPassword(password) {
  return password.length >= 8;
}

// 2. 明确导出:使用命名导出而非默认导出
// 推荐
export class UserService { }
export class AuthService { }

// 3. 避免副作用
// 模块顶层代码不应该有副作用
// 副作用应该封装在函数中

// 4. 使用barrel文件
// api/index.js
export { UserService } from './UserService.js';
export { AuthService } from './AuthService.js';
export { DataService } from './DataService.js';

// 使用
import { UserService, AuthService } from './api/index.js';

三、实战案例

3.1 模块化应用架构

javascript
// models/User.js
export class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

// services/UserService.js
import { User } from '../models/User.js';

export class UserService {
  #users = [];
  
  createUser(name, email) {
    const user = new User(name, email);
    this.#users.push(user);
    return user;
  }
  
  findUser(email) {
    return this.#users.find(u => u.email === email);
  }
}

// controllers/UserController.js
import { UserService } from '../services/UserService.js';

export class UserController {
  #userService = new UserService();
  
  async register(req) {
    const { name, email } = req.body;
    const user = this.#userService.createUser(name, email);
    return { status: 201, data: user };
  }
}

// app.js
import { UserController } from './controllers/UserController.js';

const controller = new UserController();
const result = await controller.register({
  body: { name: "Alice", email: "alice@example.com" }
});
console.log(result);

四、本章小结

第10章介绍了JavaScript模块化:ES6模块(import/export)、CommonJS(require/module.exports)、动态导入(import())。ES6模块是现代JavaScript的标准。


五、思考与练习

思考题

ES6模块是静态的还是动态的?这和CommonJS有什么区别?

Tree Shaking是什么?为什么ES6模块支持而CommonJS不支持?

模块的循环依赖会产生什么问题?

浏览器原生支持ES6模块吗?需要什么配置?

编程练习

将一个大型脚本重构为模块化结构。

实现一个插件系统(使用动态导入)。

使用模块实现一个简单的依赖注入容器。


10/14模块
0%