require gitlabjavascript
require最終會把每一個模塊都轉化爲對象html
function Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; updateChildren(parent, this, false); this.filename = null; this.loaded = false; this.children = []; }
用assert斷言輸入的合法性並調用_load方法還有一個調用_load的是java
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 這條命令調用的node
這裏有不少是關於處理main的,核心的一段是git
if (isMain) { process.mainModule = module; module.id = '.'; }
這個佐證了上面runMain是給node xxx.js 這條命令調用的這個論點,另外main模塊的id必定是 '.',而且parent必定是空。
另外在調用前會先查找模塊緩存的是否存在。
如下代碼爲了簡化刪去main模塊的處理github
//建立沒有原型的空對象 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的 exportsjson
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文件夾緩存
js,json,node,mjs
每一個後綴的文件都有對應的打開方式
js 清除可能的BOM頭後加載
json json Parse
node .node 這是C/C++編寫的擴展文件,經過dlopen()方法加載最後編譯生成的文件,能夠看成是一個系統調用app
這部分代碼比較多,只看一段註釋便可
// 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>gitlab
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; }
如何查找到的文件基本清楚了,以後就是最經常使用的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同步的特色也是在這體現出來的