CJS 和 ESM
基本点
- 标准制订
- CJS
- 社区标准
- 新增 API
Node.js 社区推动,由于是社区,所以无法改动语法,仅在 API 层面做新增
- ESM
- 官方标准
- 新增语法
- CJS
- 时态
- CJS
- 运行时
- 动态依赖:模块放置在函数中,运行时才能确定依赖关系
- 同步加载:依赖是同步加载的(涉及到 IO 操作)
- 运行时
- ESM
- 编译时(静态依赖,在代码运行前就确定了依赖关系)
- 运行时
- CJS
- 运行环境
- CJS
- 仅限于 Node.js 环境
- ESM(都支持)
- 浏览器环境
- Node.js 环境
- CJS
- 引用
- CJS
- 普通函数调用和复制
- ESM
- 符号绑定
- CJS
1. CommonJS
伪代码实现:
function require(path) {
if (/* 该模块存在缓存吗? */) {
return /* 缓存 */
}
function _run(exports, require, module, __filename, __dirname) {
/* 模块代码 */
}
var module = { exports: {} }
var exports = module.exports
_run.call(
module.exports, // 所以模块中 `this` 指向 module.exports
exports,
require,
module,
/* 模块路径 */
/* 模块目录 */
)
/* 把 module.exports 加入缓存 */
return module.exports
}
相关面试题
下面模块最后导出的结果是什么?
exports.a = 'a'
module.exports.b = 'b'
this.c = 'c'
module.exports = {
d: 'd'
}
答案
{
d: 'd'
}
2. ESM
关于符号绑定(Symbol Binding),在 ESM 中,export
并不是拷贝,而是绑定,比如:
// other.js
export let count = 1;
export const increment = () => count++;
// main.js
import { count, increment } from './other.js';
console.log('count: ', count); // Output: 1
increment();
console.log('count: ', count); // Output: 2
首先这里导入的 count
是只读的,其次 increment
函数可以修改 count
的值,并且会实时绑定(Live Binding),所以在 increment()
执行后,count
的值会实时更新。
相关面试题
export
和 export default
的区别是什么?
答案
- 导出数量
export
可以导出多个export default
一个模块只能有一个
- 导出命名
export
需要具名导出export default
默认导出,可以不具名
下面代码最后导出的结果是什么?
// other.js
export let count = 1;
export const increment = () => count++;
// main.js
import { count, increment } from './other.js';
import * as counter from './other.js';
const { count: sameCount } = counter;
increment();
console.log('count: ', count);
console.log('counter.count: ', counter.count);
console.log('sameCount: ', sameCount);
答案
count: 2
counter.count: 2
sameCount: 1
Hint
只有导入的时候才具有符号绑定,其余都为正常赋值