javascript模块化
一、CommonJS
CommonJS 是 JavaScript 社区为了解决模块化问题而提出的一种早期规范,它也是 Node.js 环境默认采用的服务器端模块化标准。在 ES6 官方推出原生的模块化方案(ESM)之前,CommonJS 是 Node.js 生态中最主流的模块组织方式。
以下是关于 CommonJS 的核心知识梳理:
基础语法:
// 变量名自定义,接收模块导出的内容
const 模块名 = require('模块标识');
💡 核心语法与特性
CommonJS 的核心非常简单,主要包含两个关键字和三个重要特性:
- 导入与导出:使用
require()导入模块,使用module.exports或exports导出模块。 - 私有作用域:每个
.js文件都是一个独立的模块,拥有自己的作用域。模块内部定义的变量、函数默认都是私有的,不会污染全局环境,只有通过module.exports暴露出去的接口才能被外部访问。 - 同步加载:执行
require()时会立即同步加载并执行目标模块。这种方式非常适合 Node.js 服务端(因为文件都在本地硬盘,读取速度极快),但在浏览器端会因为网络请求导致页面阻塞。 - 值的拷贝:CommonJS 模块导出的是“值的拷贝”(浅拷贝)。也就是说,一旦模块被导出,如果模块内部后续修改了某个变量的值,外部通过
require()引入的地方是无法感知到这个变化的。 - 模块缓存:模块在第一次被
require()后会被缓存起来。后续再次引用同一个模块时,会直接返回缓存的结果,而不会重新加载和执行代码。
实战代码如下:

let x = 5;
let addx = function (value) {
return value + x;
}
module.exports.x = x ;
module.exports.addx = addx;

const example = require('./example')
console.log(example)
console.log(example.x)
console.log(example.addx(5))
代码写好之后,在code.js界面中点击右键运行,运行结果如下所示:

再看一个实例,如下:
// math.js (导出模块)
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
// 方式一:整体导出一个对象
module.exports = {
add,
subtract
};
// 方式二:通过 exports 扩展导出(注意不能直接给 exports 赋值)
// exports.add = add;
// exports.subtract = subtract;
// main.js (导入模块)
const math = require('./math'); // 同步加载本地的 math 模块
console.log(math.add(5, 3)); // 输出: 8
console.log(math.subtract(5, 3)); // 输出: 2
下面分析一下CommonJs模块的加载机制:
-commonjs是运行时加载,第一次加载时运行了一次,输出的是值的拷贝;
-es6模块是编译时输出接口;
下面代码对commonjs中的值的拷贝进行说明,如下:

let x = 5;
let addx = function (value) {
return value + x;
}
module.exports.x = x ;
module.exports.addx = addx;
// 说明commonjs值的拷贝的代码
let counter = 3;
function inCounter() {
counter++;
}
module.exports.counter = counter;
module.exports.incounter = inCounter;

const example = require('./example')
console.log(example)
console.log(example.x)
console.log(example.addx(5))
// commonjs值的拷贝的代码
console.log(example.counter); // 3
example.incounter()
console.log(example.counter) // 虽然incounter()执行了自加,但仍然是3
继续在code.js中右键运行,输出结果如下:

二、没有const 模块名 ,只有require('模块标识')
只写 require('模块标识') 不赋值变量的用法
这种写法叫只执行模块,不接收导出值,非常常见。
1、原理
require() 本身会立刻执行目标模块里的所有代码,
module.exports,代码照样跑。示例 1:只引入、不接收返回值
新建
a.js// a.js
console.log('我被执行了');
function fn() {
console.log('函数执行');
}
// 也可以不写 module.exports
入口文件
index.js// 不写 const xx = ,直接写
require('./a');
运行输出:
我被执行了
👉 效果:加载并执行 a.js 全部代码,但不用它导出的东西。
2、常见使用场景
2.1. 引入全局配置、一次性初始化模块
很多框架 / 配置文件只需要加载一次初始化,不用拿返回值:
// 加载并执行数据库连接、全局配置
require('./config/db');
require('./config/global');
2.2. 引入扩展原型、全局补丁模块
有些模块会直接修改全局对象、原型,不需要接收变量:
// 给 Promise、数组扩展方法
require('bluebird');
require('./extend/array');
2.3. Webpack/Node 入口注册路由、事件
// 一次性注册所有路由文件
require('./routes/user');
require('./routes/goods');
3、和赋值写法的区别
// 1. 执行模块 + 接收导出对象
const a = require('./a');
// 2. 只执行模块,不接收导出
require('./a');
- 两种都会完整执行模块代码
- 区别只在:要不要用它暴露的方法 / 变量
4、一句话总结
require('模块') 单独写不赋值:
三、ES6模块
ES6 模块(ES Module,简称 ESM)是 JavaScript 官方在 ES6 (ECMAScript 2015) 标准中推出的原生模块化解决方案。它彻底统一了前端与 Node.js 的模块化规范,是现代 JavaScript 工程化开发的基石。
相比于你之前了解的 CommonJS、AMD 和 CMD,ESM 的核心设计理念是“静态化”,即在代码编译阶段就能确定模块之间的依赖关系,从而带来极高的执行效率和强大的工程优化能力。
💡 核心语法与特性
ESM 的核心由
export(导出)和 import(导入)两个命令构成。- 两种导出方式:
- 命名导出 (Named Export):一个模块可以多次使用
export导出多个变量、函数或类。导入时必须使用大括号{}包裹对应的名称。 - 默认导出 (Default Export):一个模块只能有一个
export default。导入时可以自定义任意名称,且不需要大括号。
- 命名导出 (Named Export):一个模块可以多次使用
- 静态分析:
import和export必须写在模块的顶层作用域,不能在if语句或函数内部使用。这使得打包工具(如 Webpack、Vite)能在构建阶段进行依赖分析和优化(例如 Tree Shaking 摇树优化,剔除未使用的代码)。 - 值的动态引用 (Live Binding):ESM 导出的不是值的拷贝,而是对原始变量的动态引用。当模块内部的变量发生变化时,所有引入该变量的地方都会同步更新。
- 严格模式:ESM 自动开启严格模式 (
'use strict'),无需手动声明,避免了全局变量污染等常见问题。
基础代码示例
// math.js (导出模块)
// 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
// 默认导出
export default function multiply(a, b) {
return a * b;
}
// main.js (导入模块)
// 混合导入:默认导出直接写变量名,命名导出用 {} 包裹
import multiply, { PI, add } from './math.js'; // 浏览器环境通常需要带 .js 后缀
console.log(multiply(5, 3)); // 输出: 15
console.log(add(5, 3)); // 输出: 8
console.log(PI); // 输出: 3.14159

浙公网安备 33010602011771号