CommonJS, AMD, CMD都是JS模块化的规范。
CommonJS是服务器端js模块化的规范,NodeJS是这种规范的实现。
AMD(异步模块定义)和CMD(通用模块定义)都是浏览器端js模块化的规范。RequireJS 遵循的是 AMD,SeaJS 遵循的是 CMD。
CommonJS的核心思想就是通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或者 module.exports 来导出需要暴露的接口。
function Module(id, parent) {
this.id = id; //模块的识别符,通常是带有绝对路径的模块文件名
this.exports = {}; // 表示模块对外输出的值,默认值为空对象
this.parent = parent; //返回一个对象,表示调用该模块的模块
this.filename = null; //模块的文件名,带有绝对路径
this.loaded = false; // 返回一个布尔值,表示模块是否加载
this.children = []; //返回一个数组,表示该模块要用到的其他模块
}
module.exports = Module;
let module = new Module(filename, parent)
(function(exports, require, module, __filename, __dirname) {
//导出方法
module.exports = exports = function sum(a, b) {
return a + b;
};
//不能改变exports指向,因为返回的是module.exports,所以是个{}
return module.exports;
});
//操作文件的模块
let fs = require('fs')
// 处理路径的模块
let path = require('path')
// 虚拟机模块,沙箱运行,防止变量污染
let vm = require('vm')
//该对象为不同的文件类型声明对应的处理函数(这里只是大概描述一下,之后会完善该对象)
Module._extensions = { js: () => {}, json: () => {} };
Module._resolveFilename = r_path => {
//如果相对路径有后缀名js或json,则返回绝对路径
if (/\.js$|\.json$/.test(r_path)) {
return path.resolve(__dirname, r_path);
} else {
let exts = Object.keys(Module._extensions); //取得所有可能的后缀名
let real_name;
for (let i = 0; i < exts.length; i++) {
try {
//查看该文件是否存在
fs.accessSync(r_path + '.' + exts[i]);
real_name = r_path + exts[i];
} catch (error) {}
}
//如果找到了该文件
if (real_name) {
return path.resolve(__dirname, real_name);
} else {
throw new Error('module not exist');
}
}
};
//缓存对象
Module._cacheModules = {};
//命中则返回缓存中的模块,没命中则新建一个模块对象且加入缓存
Module.catchCache = real_path => {
if (Module._cacheModules[real_path]) {
return Module._cacheModules[real_path];
} else {
Module._cacheModules[r_path] = new Module(r_path);
return Module._cacheModules[r_path];
}
};
//前面所提到的闭包的字符串表示
Module.wrapper = ['function(exports,require,module){', '}'];
//读取文件内容作为闭包函数体
Module.wrap = content => {
return Module.wrapper[0] + content + Module.wrapper[1];
};
//完善_extensions
Module._extensions = {
js: module => {
//同步读取文件
let content = fs.readFileSync(module.id, 'utf8');
//拼接成完整的函数
let func_str = Module.wrapper(content);
//沙箱运行该函数
vm.runInThisContext(func_str).call(
module.exports,
module.exports,
require,
module
);
},
json: module => {
//将字符串转为JSON,并赋值给module.exports
let content = fs.readFileSync(module.id, 'utf8');
module.exports = JSON.parse(content);
}
};
function tryModuleLoad(module) {
//获取后缀
let ext = path.extname(module.id);
//根据文件类型用相应的函数处理
Module._extensions[ext](module);
}
Module._load = r_path => {
//获取绝对路径
let real_path = Module._resolveFilename(r_path);
//命中缓存或新建模块
let module = Module.catchCache(real_path);
//将文件中导出的赋值给module.exports
tryModuleLoad(module);
return module.exports;
};
//最终的require API
function require(r_path) {
return Module._load(r_path);
}
let a = require('./a.js');
exports.sum = (a, b) => {
console.log(a + b);
};