-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
commonJS
什么是 commonJS
[Modules: CommonJS modules | Node.js v22.2.0 Documentation]
commonJS 是一种前端模块化规范
-
每个文件有自己单独的作用域,在其中定义的所有变量、函数、类都是私有的
-
通过 module.exports 来对外暴露值、通过 require 来加载 module.exports 暴露的值
-
如果想要多文件共享,可以把功能定义到全局变量上(不推荐的🙅)
module 对象
每一个模块都有一个 module 对象,有以下属性:
const module {
id: '.',
path: 'D:\\work\\commonjs\\tests',
exports: {}, // 模块对外输出的值
filename: 'D:\\work\\commonjs\\tests\\index.js',
loaded: false, // 该模块是否加载完成
children: [], // require 的模块信息
parent: [] // 调用该模块的模块信息
paths: [ // 模块的搜索路径
'D:\\work\\commonjs\\tests\\node_modules',
'D:\\work\\commonjs\\node_modules',
'D:\\work\\node_modules',
'D:\\node_modules'
]
}
exports 对象
exports 对象是对 module.exports 的引用
var exports = module.exports
模块中的 this 也指向这个 exports 对象
require 方法
require 用来读取一个 JS 文件,并返回 exports 对象:
const bar = require('./foo.js');
// 读取 foo.js 并把 exports 赋值给 bar
文件解析规则:
-
如果路径以
/
开头,则加载绝对路径的模块文件 -
如果以
./
开头,则加载相对路径文件 -
否则加载内置的模块 或者 根据当前路径,递归查找
node_modules
下的模块文件并结合 package.json 进行解析: -
如果指定的模块文件没有发现,Node 会依次添加
.js
、.json
、.node
后缀并再次查找
具体的算法:
[Modules: CommonJS modules | Node.js v22.2.0 Documentation]
代码示例加载逻辑:
Moldule._load = function(request) {
// 1. 判断是否命中 Module.cache
// 2. 没命中的话执行 module._load 加载模块
// 3. 执行 module._compile 执行文件
// 4. 缓存执行结果,并返回 module.exports
}
Module.prototype._compile = function(content) {
const module = {
exports: {},
...
}
// 执行文件
// (function (exports, require, module, __filename, __dirname) {
// content
// })();
return module.exports
}
require 对象
const require = [Function: require] { // 既是函数也是对象
resolve: [Function: resolve] // 返回加载的绝对地址
{
paths: [Function: paths] // 获取 path 的查找路径
},
main: { // 入口模块
id: '.',
path: 'D:\\work\\commonjs\\tests',
exports: {},
filename: 'D:\\work\\commonjs\\tests\\index.js',
loaded: false,
children: [ [Object] ],
paths: [
'D:\\work\\commonjs\\tests\\node_modules',
'D:\\work\\commonjs\\node_modules',
'D:\\work\\node_modules',
'D:\\node_modules'
]
},
extensions: [Object: null prototype] { // 不同后缀执行的加载方法
'.js': [Function (anonymous)],
'.json': [Function (anonymous)],
'.node': [Function (anonymous)]
},
cache: [Object: null prototype] { // 缓存的模块信息
'D:\\work\\commonjs\\tests\\index.js': {
id: '.',
path: 'D:\\work\\commonjs\\tests',
exports: {},
filename: 'D:\\work\\commonjs\\tests\\index.js',
loaded: false,
children: [Array],
paths: [Array]
},
'D:\\work\\commonjs\\tests\\import.js': {
id: 'D:\\work\\commonjs\\tests\\import.js',
path: 'D:\\work\\commonjs\\tests',
exports: [Object],
filename: 'D:\\work\\commonjs\\tests\\import.js',
loaded: true,
children: [],
paths: [Array]
}
}
}
循环加载
// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';
// b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';
// main.js
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);
// output
// b.js a1
// a.js b2
// main.js a2
// main.js b2
Metadata
Metadata
Assignees
Labels
No labels