提起nodejs中的模块,就会想到用require去加在引用那个模块。看了不少博客,加载机制明白了,脑子里总是稀里糊涂的知道会每个文件会被'(function (exports, require, module, __filename, __dirname) {',
// 文件的源码'n});'包裹,自然也就有文件中的require命令了。前几天看了模块的源码 module.js源码的前几行const NativeModule = require('native_module');const util = require('util');const internalModule = require('internal/module');const vm = require('vm');const assert = require('assert').ok;const fs = require('fs');const internalFS = require('internal/fs');
一上来就懵了,module.js不就是为了实现require的吗?为什么一上来就用require去引用其他模块,从而陷入死循环。在一些技术网站提问 虽然还不是很明白,但得到了一些思路,然后又开始重新看源码,并动手调试,总算想清楚了。于是想写写记录自己的整个过程。
一、module.js前几行的require从哪里来的
写了两个js文件,a.js
const b = require('./b.js');
b.js
exports.done = false;
node 执行a.js
首先node启动时会先执行第一个js文件这个文件中会定义module.js中第一行的NativeModule。可以看到NativeModule的定义function NativeModule(id) {this.filename = `${id}.js`;this.id = id;this.exports = {};this.loaded = false;this.loading = false;
}
从入口函数startup中可以看到
const Module = NativeModule.require('module');也就是说会去加载module.js模块。
在require函数中
NativeModule.require = function(id) {if (id === 'native_module') { return NativeModule;}/..../const nativeModule = new NativeModule(id);新建NativeModule对象nativeModule.cache();nativeModule.compile(); //主要步骤return nativeModule.exports;
};
在compile中
NativeModule.prototype.compile = function() {var source = NativeModule.getSource(this.id);source = NativeModule.wrap(source); this.loading = true;try { const fn = runInThisContext(source, { filename: this.filename, lineOffset: 0, displayErrors: true }); fn(this.exports, NativeModule.require, this, this.filename); this.loaded = true;} finally { this.loading = false;}
};
wrap会进行文件的包裹
NativeModule.wrap = function(script) {return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];};NativeModule.wrapper = ['(function (exports, require, module, __filename, __dirname) { ','\n});'];
这里的require是NativeModule.require
接下来就会执行Module.runMain函数,从而进入module.js中,所以module
.js中开始的require是NativeModule.require,并不矛盾。二、a.js的执行情况
执行a.js时则会通过bootstrap_node.js的runMain函数进入module.jsModule.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 programprocess._tickCallback();};调用Module._load函数,process.argv[1]为a.js
Module.runMain->Module._load->tryModuleLoad->module.load->Module._extensions['.js']->module._compile
在module._compile中
var wrapper = Module.wrap(content);这个时候才调用NativeModule的wrap函数对a.js就行包裹
接下来
var require = internalModule.makeRequireFunction.call(this);会通过 https://github.com/nodejs/node/blob/master/lib/internal/module.js中的makeRequireFunction函数创造一个require函数, function require(path) { try { exports.requireDepth += 1; return self.require(path);} finally { exports.requireDepth -= 1;}
}
call.(this)将指针指向module,从而a.js包裹的头部中require就是makeRequireFunction返回的require, self.require(path);则会调用用Module.prototype.requireModule.prototype.require = function(path) {return Module._load(path, this, /* isMain */ false);};require则会调用 Module._load去加载其他模块。
这就是几个require的关系。
新人第一次写,如有错误,还请纠正。
参考: