本文是上文《Node.js 启动方式:一道关于全局变量的题目引发的思考》的续章。
原题回顾
我们还是先回顾下原题吧。
1 2 3 4 5 6 |
var a = 2; function foo(){ console.log(this.a); } foo(); |
上题由我们亲爱的小龙童鞋发现并在我们的 901 群里提问的。
不过在上面一篇文章中,我们讲的是在 REPL 和 vm
中有什么事情,但是并没有解释为什么在文件模块的载入形式下,var
并不会挂载到全局变量去。
其实原因很简单,大家应该也都明白,在 Node.js 中,每个文件相当于是一个闭包,在 require
的时候被编译包了起来。
但是具体是怎么样的呢?虽然网上也有很多答案,我还是决定在这里按上一篇文章的尿性稍微解释一下。
分析
首先我们还是回到上一篇文章的《Node REPL 启动的沙箱》一节,里面说了当启动 Node.js 的时候是以 src/node.js 为入口的。
如果以 REPL 为途径启动的话是直接启动一个 vm
,而此时的所有根级变量都在最顶级的作用域下,所以一个 var
自然会绑定到 global
下面了。
而如果是以文件,即 $ node foo.js
形式启动的话,它就会执行 src/node.js 里面的另一坨条件分支了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// ... } else if (process.argv[1]) { // make process.argv[1] into a full path var path = NativeModule.require('path'); process.argv[1] = path.resolve(process.argv[1]); var Module = NativeModule.require('module'); // ... startup.preloadModules(); if (global.v8debug && process.execArgv.some(function(arg) { return arg.match(/^--debug-brk(=[0-9]*)?$/); })) { var debugTimeout = +process.env.NODE_DEBUG_TIMEOUT || 50; setTimeout(Module.runMain, debugTimeout); } else { // Main entry point into most programs: Module.runMain(); } } else { // ... |
从上面的代码看出,只要是以 $ node foo.js
形式启动的,都会经历 startup.preloadModules()
和 Module.runMain()
两个函数。
startup.preloadModules()
我们来看看这个函数。
1 2 3 4 5 |
startup.preloadModules = function() { if (process._preload_modules) { NativeModule.require('module')._preloadModules(process._preload_modules); } }; |
实际上就是执行的 lib/module.js 里面的 _preloadModules
函数,并且把这个 process._preload_modules
给传进去。当然,前提是有这个 process._preload_modules
。
process._preload_modules
这个 process._preload_modules
指的就是当你在使用 Node.js 的时候,命令行里面的 --require
参数。
1 |
-r, --require module to preload (option can be repeated) |
代码在 src/node.cc 里面可考。
1 2 3 4 5 6 7 8 9 10 11 12 |
// ... } else if (strcmp(arg, "--require") == 0 || strcmp(arg, "-r") == 0) { const char* module = argv[index + 1]; ="crayon-5812f43763e43072072956" class="crayon-syntax crayon-theme-github crayon-font-monaco crayon-os-pc print-yes notranslate" data-settings=" minimize scroll-always" style=" margin-top: 12px; margin-bottom: 12px; font-size: 13px !important; line-height: 15px !important;">
不过在上面一篇文章中,我们讲的是在 REPL 和 其实原因很简单,大家应该也都明白,在 Node.js 中,每个文件相当于是一个闭包,在 但是具体是怎么样的呢?虽然网上也有很多答案,我还是决定在这里按上一篇文章的尿性稍微解释一下。 分析首先我们还是回到上一篇文章的《Node REPL 启动的沙箱》一节,里面说了当启动 Node.js 的时候是以 src/node.js 为入口的。 如果以 REPL 为途径启动的话是直接启动一个 而如果是以文件,即
从上面的代码看出,只要是以 startup.preloadModules()我们来看看这个函数。
实际上就是执行的 lib/module.js 里面的 process._preload_modules这个
代码在 src/node.cc 里面可考。
|