两本书不黑晶版:
两本书漂亮版:215566435/Fz-node,讨厌的 ,吕方
深入细致下层:Node.js开启和组件读取
以后早已从js微观看了 nodejs 标识符,所以那时太少说,间接上看深入细致至 c++下层的源标识符.
从赫赫有名的main早已开始
//node_main.cc
int main(int argc, char *argv[]) {
return node::Start(argc, argv);
}
去除许多网络平台推论代码,他们走进了最有名的c词汇表达式,那个表达式只不过而已为的是带出node::Start(argc, argv);,他们竭尽全力深入细致进上看
int Start(int argc, char** argv) {
//…
V8::Initialize();
const int exit_code = Start(uv_default_loop(), argc, argv, exec_argc, exec_argv);
//….
V8::Dispose();
v8_platform.Dispose();
//….
return exit_code;
}
oop()之后,又走进了一个start表达式。
//….
LoadEnvironment(&env);
{
//….
do {
//事件循环在这里才早已开始
uv_run(env.event_loop(), UV_RUN_DEFAULT);
more = uv_loop_alive(env.event_loop());
} while (more == true);
在深度遍历了几个Start之后,他们走进第一个重要的表达式。那个函数做的事情只不过就是读取他们node.js的乱七八糟组件以及跑他们的一早已开始的标识符了。什么意思呢?只不过就是node hello.js,第一遍跑他们的标识符没有进入事件循环时,就会跑那个标识符,再一次证明了,我们的标识符执行一早已开始,并不会进入事件循环,而是跑完所有同步标识符以后,才会早已开始。
/*
在最近的版中,bootstrap.js被拆分成了loader.js和node.js
再这里他们看到了v8读取javascript的方法 */
Local<String> loaders_name =
FIXED_ONE_BYTE_STRING(env->isolate(), “internal/bootstrap/loaders.js”);
Local<Function> loaders_bootstrapper =
GetBootstrapper(env, LoadersBootstrapperSource(env), loaders_name);
Local<String> node_name =
FIXED_ONE_BYTE_STRING(env->isolate(), “internal/bootstrap/node.js”);
Local<Function> node_bootstrapper =
GetBootstrapper(env, NodeBootstrapperSource(env), node_name);
// Add a reference to the global object Local<Object> global = env->context()->Global();
// Bootstrap internal loaders Local<Value> bootstrapped_loaders;
if (!ExecuteBootstrapper(env, loaders_bootstrapper,
arraysize(loaders_bootstrapper_args),
loaders_bootstrapper_args,
&bootstrapped_loaders)) {
return;
}
// Bootstrap Node.js
Local<Value> bootstrapped_node;
Local<Value> node_bootstrapper_args[] = {
env->process_object(),
bootstrapped_loaders
};
if (!ExecuteBootstrapper(env, node_bootstrapper,
arraysize(node_bootstrapper_args),
node_bootstrapper_args,
&bootstrapped_node)) {
以上标识符只不过就是做了几件事情:
1. 初始化global对象
2. 读取bootstrap中的两个组件
3. 挂载bootstrap初始化之后的东西到global对象中
所以,神秘的bootstrap两个组件到底是什么呢?让他们一探究竟
bootstrap/loader.js
(function bootstrapInternalLoaders(process, getBinding, getLinkedBinding,
getInternalBinding) {
});
loader是一个表达式表达式,注意这里使用了(function(){})的方式将源标识符包住,究其原因是为的是让V8导出的时候,告诉V8把这段标识符导出成一个c++的表达式表达式,具体映射到c++,标识符主要是
//读取源标识符字符串,你看这是String类型
Local<String> loaders_name =
FIXED_ONE_BYTE_STRING(env->isolate(), “internal/bootstrap/loaders.js”);
on类型//说明loaders_bootstrapper就是一个表达式
Local<Function> loaders_bootstrapper =
GetBootstrapper(env, LoadersBootstrapperSource(env), loaders_name);
//注意看,这里是的参数只不过就是对应了bootstrapInternalLoaders中的4个参数
Local<Value> loaders_bootstrapper_args[] = {
env->process_object(),
get_binding_fn,
get_linked_binding_fn,
get_internal_binding_fn
};
//执行ExecuteBootstrapper
Local<Value> bootstrapped_loaders;
if (!ExecuteBootstrapper(env, loaders_bootstrapper,
arraysize(loaders_bootstrapper_args),
loaders_bootstrapper_args,
&bootstrapped_loaders)) {
思路很简单,就是标识符一大坨而已,接下来他们看看bootstrapInternalLoaders中四个重要的参数是什么
(function bootstrapInternalLoaders(process, getBinding, getLinkedBinding,
getInternalBinding) {
});
//process你没看错,就是他们所用的全局process对象
//getBinding只不过就是之后的process.binding//getLinkedBinding用于绑定在process._getLinkedBinding上用于载入c++组件,比如用户写的c++ addon
//getInt
他们往下遍历标识符就会看到他们的老朋友
// Set up NativeModule
function NativeModule(id) {
this.filename = `${id}.js`;
this.id = id;
this.exports = {};
this.loaded = false;
this.loading = false;
}
//构造一个loader,之后用于导出 const loaderExports = { internalBinding, NativeModule };
NativeModule.require = function(id) {
const nativeModule = new NativeModule(id);
nativeModule.cache();
nativeModule.compile();
return nativeModule.exports;
}
return loaderExports;
NativeModule组件。那个组件只不过就是他们用在Node.js中的module定义了,可以看见,里面的this.exports.他们往下看,终于见到了他们的老朋友require,所以在他们一早已开始调用node hello.js时的require是在这里被创建的,也就是Node.js开启的时候。而require之后的结果,永远是被编译过后的eports对象.
在最后,导出那个loader,还给c++层,然后将loader和process,传递给bootstrap/node.js.
bootstrap/node.js
(function bootstrapNodeJSCore(process, { internalBinding, NativeModule }){
NativeModule.require(internal/process/warning).setup();
NativeModule.require(internal/process/next_tick).setup();
NativeModule.require(internal/process/stdio).setup();
//…..
evalScript(xxx)//执行他们的标识符
}
bootstrapNodeJSCore就是他们的开启表达式了,那个脚本跑完,所有的同步标识符会被执行完毕我,他们看到这里传递进来了process和他们需要的第一个NativeModule
在这段表达式中,只不过做的就是初始化,比如读取console,记载next_tick等等…他们用户指定的标识符由`evalScript进行调用
function evalScript(name) {
const CJSModule = NativeModule.require(internal/modules/cjs/loader);
const path = NativeModule.require(path);
const cwd = tryGetCwd(path);
const module = new CJSModule(name);
module.filename = path.join(cwd, name);
module.paths = CJSModule._nodeModulePaths(cwd);
const body = wrapForBreakOnFirstLine(process._eval);
const script = `global.__filename = ${JSON.stringify(name)};\n` +
global.exports = exports;\n +
global.module = module;\n +
global.__dirname = __dirname;\n +
global.require = require;\n +
return require(“vm”).runInThisContext( +
`${JSON.stringify(body)}, { filename: ` +
`${JSON.stringify(name)}, displayErrors: true });\n`;
const result = module._compile(script, `${name}-wrapper`);
if (process._print_eval) console.log(result);
// Handle any nextTicks added in the first tick of the program.
process._tickCallback();
}
至此,他们揭开了所有谜团,在v8编译这段标识符运行的时候,就会被evalScript,以字符串拼接的方式,将他们的标识符拼接到这里,然后使用vm.runInThisContext()的办法去跑他们的code,所以他们的第一次跑就会执行了。在这里值得注意的是,在初始化的时候就会调用,_tickCallback方法。
从头梳理
node.js开启的时候,会从c++微观先开启,然后走到bootstrap里面初始化流程是初始化global对象->读取编译内置组件(process,c++等等)->运行用户指定脚本->跑一次nextTick->进入事件循环本章内容最好和以后的一起看
1. 组件化js层:实现一个简单组件读取2. 组件化js层2:组件读取之谜