mudule对象化
require终究会把每一个模块都转化为对象
function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
updateChildren(parent, this, false);
this.filename = null;
this.loaded = false;
this.children = [];
}
require要领
用assert断言输入的合法性并挪用_load要领另有一个挪用_load的是
Module.runMain = function() {
// Load the main module--the command line argument.
Module._load(process.argv[1], null, true);
// Handle any nextTicks added in the first tick of the program
process._tickCallback();
};
这个我不是迥殊肯定,但基础肯定是给node xxx.js 这条敕令挪用的
load要领
这里有很多是关于处置惩罚main的,中心的一段是
if (isMain) {
process.mainModule = module;
module.id = '.';
}
这个左证了上面runMain是给node xxx.js 这条敕令挪用的这个论点,别的main模块的id肯定是 ‘.’,而且parent肯定是空。
别的在挪用前会先查找模块缓存的是不是存在。
以下代码为了简化删去main模块的处置惩罚
//建立没有原型的空对象
Module._cache = Object.create(null);
Module._pathCache = Object.create(null);
Module._load = function(request, parent, isMain) {
if (parent) {
debug('Module._load REQUEST %s parent: %s', request, parent.id);
}
var filename = Module._resolveFilename(request, parent, isMain);
//缓存中是不是存在
var cachedModule = Module._cache[filename];
if (cachedModule) {
updateChildren(parent, cachedModule, true);
return cachedModule.exports;
}
//是不是是native模块
if (NativeModule.nonInternalExists(filename)) {
debug('load native module %s', request);
return NativeModule.require(filename);
}
//其他模块处置惩罚
var module = new Module(filename, parent);
Module._cache[filename] = module;
tryModuleLoad(module, filename);
return module.exports;
};
这个部份说清晰明了加载模块首先是推断模块名,以后是查找缓存,查找native 模块,然后是其他模块,末了的return是最为症结的,返回值永远是module的 exports
查找node_modules文件夹的划定规矩
Module._nodeModulePaths = function(from) {
// guarantee that 'from' is absolute.
from = path.resolve(from);
// Return early not only to avoid unnecessary work, but to *avoid* returning
// an array of two items for a root: [ '//node_modules', '/node_modules' ]
if (from === '/')
return ['/node_modules'];
// note: this approach *only* works when the path is guaranteed
// to be absolute. Doing a fully-edge-case-correct path.split
// that works on both Windows and Posix is non-trivial.
const paths = [];
var p = 0;
var last = from.length;
for (var i = from.length - 1; i >= 0; --i) {
const code = from.charCodeAt(i);
if (code === 47//*/*/) {
if (p !== nmLen)
paths.push(from.slice(0, last) + '/node_modules');
last = i;
p = 0;
} else if (p !== -1) {
if (nmChars[p] === code) {
++p;
} else {
p = -1;
}
}
}
从from最先逐层向上查找node_modules文件夹
Module._extensions
js,json,node,mjs
每一个后缀的文件都有对应的打开方式
js 消灭能够的BOM头后加载
json json Parse
node .node 这是C/C++编写的扩大文件,经由历程dlopen()要领加载末了编译天生的文件,能够看成是一个体系挪用
findPath
这部份代码比较多,只看一段解释即可
// given a module name, and a list of paths to test, returns the first
// matching file in the following precedence.
//
// require(“a.<ext>”)
// -> a.<ext>
//
// require(“a”)
// -> a
// -> a.<ext>
// -> a/index.<ext>
package.json
node会去寻觅途径中的package.json文件而且会加载个中的main,并放入packagecache中,用main中指定的文件再去确认绝对途径然后加载
function readPackage(requestPath) {
const entry = packageMainCache[requestPath];
if (entry)
return entry;
const jsonPath = path.resolve(requestPath, 'package.json');
const json = internalModuleReadFile(path.toNamespacedPath(jsonPath));
if (json === undefined) {
return false;
}
try {
var pkg = packageMainCache[requestPath] = JSON.parse(json).main;
} catch (e) {
e.path = jsonPath;
e.message = 'Error parsing ' + jsonPath + ': ' + e.message;
throw e;
}
return pkg;
}
compile
怎样查找到的文件基础清晰了,以后就是最经常使用的js的compile了
js模块的外层肯定会被套上
Module.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
以后触及了断点的处置惩罚,是在vm模块中的Srcript对象中的runInThisContext
背面有一句说清晰明了为何node中的一切文件也能够具有Module的种种要领和特征 require 包含那些并非main的文件,别的一切的模块也是公用的模块缓存,应用Module require中的return Module._load(path, this, /* isMain */ false);
把本身的this作为parent作为Module对象的parentvar require = internalModule.makeRequireFunction(this);
makeRequireFunction的代码
// Invoke with makeRequireFunction(module) where |module| is the Module object
// to use as the context for the require() function.
function makeRequireFunction(mod) {
const Module = mod.constructor;
function require(path) {
try {
exports.requireDepth += 1;
return mod.require(path);
} finally {
exports.requireDepth -= 1;
}
}
function resolve(request, options) {
return Module._resolveFilename(request, mod, false, options);
}
require.resolve = resolve;
function paths(request) {
return Module._resolveLookupPaths(request, mod, true);
}
resolve.paths = paths;
require.main = process.mainModule;
// Enable support to add extra extension types.
require.extensions = Module._extensions;
require.cache = Module._cache;
return require;
}
实行compiledWrapper,对应wrapper插进去的jsresult = compiledWrapper.call(this.exports, this.exports, require, this, filename, dirname);
首先是compiledWrapper的this绑定在了Module本身exports上,本身的exports也作为了参数被注入,相当于隐式的赋值exports就是compiledWrapper中的exports了这个就成了对外唯一的输出了,其他的一切值都成了compiledWrapper的私有不会污染全局,require作为参数被注入,别的就是文件名 和途径的注入
从上述处置惩罚加载的历程我们能够发明只需有require node就会继承加载,别的CommonJs同步的特性也是在这表现出来的