<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>xadillax (機巧死月不會碼代碼)</title>
    <link>https://ruby-china.org/xadillax</link>
    <description>偽宅碼畜一枚。</description>
    <language>en-us</language>
    <item>
      <title>从暴力到 NAN 再到 NAPI——Node.js 原生模块开发方式变迁</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;本文由我首发于 &lt;a href="http://gitbook.cn/books/5938f4ae8b55d47644b7a445/index.html" rel="nofollow" target="_blank" title=""&gt;GitChat&lt;/a&gt; 中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;在 Node.js 开发领域中，原生 C++ 模块的开发一直是一个被人冷落的角落。但是实际上在必要的时候，用 C++ 进行 Node.js 的原生模块开发能有意想不到的好处。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;性能提升。很多情况下，使用 C++ 进行 Node.js 原生模块开发的性能会比纯 Node.js 开发要高，少数情况除外。&lt;/li&gt;
&lt;li&gt;开发成本节约。在一些即有的 C++ 代码上做封装，开发成本远远低于从零开始写 Node.js 代码。&lt;/li&gt;
&lt;li&gt;Node.js 无法完成的工作。个别情况，开发者只能得到一个库的静态连接库或者动态链接库以及一堆 C++ 头文件，其余都是黑盒的，这种情况就不得不使用 C++ 进行模块开发了。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文将从早期的 Node.js 开始，逐渐披露 Node.js 原生 C++ 模块开发方式的变迁。一直到最后，会比较详细地对 Node.js v8.x 新出的原生模块开发接口 N-API 做一次初步的尝试和解析，使得大家对 Node.js 原生 C++ 模块开发的固有印象（认为特别麻烦）有一个比较好的改观，让大家都来尝试一下 Node.js 原生 C++ 模块的开发。&lt;/p&gt;
&lt;h2 id="不变应万变"&gt;不变应万变&lt;/h2&gt;
&lt;p&gt;虽然 Node.js 原生 C++ 模块开发方式有了很大的改变，但是有一些内容是不变的，至少到现在来说都是基本上没什么 Breaking 的变化。&lt;/p&gt;
&lt;h3 id="原生模块本质"&gt;原生模块本质&lt;/h3&gt;
&lt;p&gt;这就要从 Node.js 最本质的 C++ 模块开发讲起了。举个例子，我们在 Linux 下有一个合法的原生模块 &lt;strong&gt;ons.node&lt;/strong&gt;，它其实是一个二进制文件，使用文本编辑器无法正常地看出什么鬼，直到我们遇到了二进制文件查看器。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://images.gitbook.cn/81655b10-4cbe-11e7-920d-2570ff832158" title="" alt="ons.node 二进制内容"&gt;&lt;/p&gt;

&lt;p&gt;眼尖的同学会看到它的 Magic Number[^1] 是 &lt;code&gt;0x7F454C46&lt;/code&gt;，其按位的 ASCII 码代表的字符串是 &lt;code&gt;ELF&lt;/code&gt;。于是答案呼之欲出，这就是一个 Linux 下的动态链接库文件。&lt;/p&gt;

&lt;p&gt;事实上，不只是在 Linux 中。当一个 Node.js 的 C++ 模块在 OSX 下编译会得到一个后缀是 &lt;strong&gt;*.node&lt;/strong&gt; 本质上是 &lt;strong&gt;*.dylib&lt;/strong&gt; 的动态链接库；而在 Windows 下则会得到一个后缀是 &lt;strong&gt;*.node&lt;/strong&gt; 本质上是 &lt;strong&gt;*.dll&lt;/strong&gt; 的动态链接库。&lt;/p&gt;

&lt;p&gt;这么一个模块在 Node.js 中被 &lt;code&gt;require&lt;/code&gt; 的时候，是通过 &lt;code&gt;process.dlopen()&lt;/code&gt; 对其进行引入的。我们来看一下 Node.js v6.9.4 的 &lt;code&gt;DLOpen&lt;/code&gt;[^2] 函数吧：&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DLOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;FunctionCallbackInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetCurrent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;uv_lib_t&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Utf8Value&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// 使用 uv_dlopen 加载链接库&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;is_dlopen_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uv_dlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;node_module&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modpending&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;modpending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="c1"&gt;// 将加载的链接库句柄转移到 mp 上&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_dso_handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modlist_addon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;modlist_addon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exports_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;exports_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// exports_string 其实就是 `"exports"`&lt;/span&gt;
  &lt;span class="c1"&gt;// 这句的意思是 `exports = module.exports`&lt;/span&gt;
  &lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exports_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_context_register_func&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_context_register_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_priv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_register_func&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_register_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nm_priv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;uv_dlclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ThrowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Module has no declared entry point."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按照逻辑来讲，这个加载过程其实就是下面这样的。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;通过 &lt;code&gt;uv_dlopen&lt;/code&gt; 加载链接库。&lt;/li&gt;
&lt;li&gt;将加载的链接库挂到原生模块链表中去。&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;mp-&amp;gt;nm_register_func()&lt;/code&gt; 初始化这个模块，并得到该有的 &lt;code&gt;module&lt;/code&gt; 和 &lt;code&gt;module.exports&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;流程走下来就跟这个流程图差不多。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://images.gitbook.cn/4c391290-4cc0-11e7-be96-bb59812ba34f" title="" alt="DLOpen 流程图"&gt;&lt;/p&gt;
&lt;h3 id="node-gyp"&gt;node-gyp&lt;/h3&gt;
&lt;p&gt;这货是 Node.js 中编译原生模块用的。自从 Node.js v0.8 之后，它就跟 Node.js 黏上了，在此之前它的默认编译帮助包是 node-waf[^3]，对于老 Noder 来说应该不会陌生的。&lt;/p&gt;
&lt;h4 id="GYP"&gt;GYP&lt;/h4&gt;
&lt;p&gt;node-gyp 是基于 GYP[^4] 的。它会识别包或者项目中的 &lt;strong&gt;binding.gyp&lt;/strong&gt;[^5] 文件，然后根据该配置文件生成各系统下能进行编译的项目，如 Windows 下生成 Visual Studio 项目文件（&lt;strong&gt;*.sln&lt;/strong&gt; 等），Unix 下生成 Makefile。在生成这些项目文件之后，node-gyp 还能调用各系统的编译工具（如 GCC）来将项目进行编译，得到最后的动态链接库 &lt;strong&gt;*.node&lt;/strong&gt; 文件。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;从上面的描述中大家可以看到，Windows 下编译 C++ 原生模块是依赖 Visual Studio 的，这就是为什么大家在安装一些 Node.js 包的时候会需要你事先安装好 Vusual Studio 了。&lt;/p&gt;

&lt;p&gt;事实上，对于并没有 Visual Studio 需求的同学们来说，它不是必须的，毕竟 node-gyp 只依赖它的编译器，而不是 IDE。想要精简化安装的同学可以直接访问 &lt;a href="http://landinghub.visualstudio.com/visual-cpp-build-tools" rel="nofollow" target="_blank"&gt;http://landinghub.visualstudio.com/visual-cpp-build-tools&lt;/a&gt; 下载 Visual CPP Build Tools 安装，或者通过 &lt;code&gt;$ npm install --global --production windows-build-tools&lt;/code&gt; 命令行的方式安装，就能得到你该得到的编译工具了。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;说了那么多，让大家见识一下 &lt;strong&gt;binding.gyp&lt;/strong&gt; 的基本结构吧。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gyp&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;targets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;target_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addon1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sources&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1/addon.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1/myobject.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;target_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addon2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sources&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2/addon.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2/myobject.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;target_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addon3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sources&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3/addon.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3/myobject.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;target_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addon4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sources&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4/addon.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4/myobject.cc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段配置讲述了这么一个故事：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;定义了 4 个 C++ 原生模块。&lt;/li&gt;
&lt;li&gt;每个模块的源码分别是 &lt;strong&gt;*/addon.cc&lt;/strong&gt; 和 &lt;strong&gt;*/myobject.cc&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;4 个模块名分别是 &lt;strong&gt;addon1&lt;/strong&gt; 至 &lt;strong&gt;addon4&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;隐藏故事：通过正规途径编译好后，这些模块存在于 &lt;strong&gt;build/Release/addon*.node&lt;/strong&gt; 中。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于 GYP 配置文件的更多内容，大家可自行去官方文档观摩，在脚注中有 GYP 的链接。&lt;/p&gt;
&lt;h4 id="做的事情"&gt;做的事情&lt;/h4&gt;
&lt;p&gt;node-gyp 除了自身是基于 GYP 的之外，它还做了一些额外的事情。首先，在我们编译一个 C++ 原生扩展的时候，它会去指定目录下（通常是 &lt;strong&gt;~/.node-gyp&lt;/strong&gt; 目录下）搜我们当前 Node.js 版本的头文件和静态连接库文件，若不存在，它就会火急火燎跑去 Node.js 官网下载。&lt;/p&gt;

&lt;p&gt;这是一个 Windows 下 node-gyp 下载的指定版本 Node.js 头文件和库文件的目录结构。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://images.gitbook.cn/fca37040-4cc4-11e7-920d-2570ff832158" title="" alt="Windows 下的 Node.js 头文件与库文件"&gt;&lt;/p&gt;

&lt;p&gt;这个头文件目录会在 node-gyp 进行编译时，以 &lt;code&gt;"include_dirs"&lt;/code&gt; 字段的形式合并进我们事先写好的 &lt;strong&gt;binding.gyp&lt;/strong&gt; 中，总而言之，这里面的所有头文件能被直接 &lt;code&gt;#include &amp;lt;&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id="子命令"&gt;子命令&lt;/h4&gt;
&lt;p&gt;node-gyp 是一个命令行的程序，在安装好后能通过 &lt;code&gt;$ node-gyp&lt;/code&gt; 直接运行它。它有一些子命令供大家使用。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$ node-gyp configure&lt;/code&gt;：通过当前目录的 binding.gyp 生成项目文件，如 Makefile 等；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$ node-gyp build&lt;/code&gt;：将当前项目进行构建编译，前置操作必须先 &lt;code&gt;configure&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$ node-gyp clean&lt;/code&gt;：清理生成的构建文件以及输出目录，说白了就是把目录清理了；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$ node-gyp rebuild&lt;/code&gt;：相当于依次执行了 &lt;code&gt;clean&lt;/code&gt;、&lt;code&gt;configure&lt;/code&gt; 和 &lt;code&gt;build&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$ node-gyp install&lt;/code&gt;：手动下载当前版本的 Node.js 的头文件和库文件到对应目录。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="时代在召唤"&gt;时代在召唤&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;第 N 套国际 Node.js 开发者原生 C++ 模块开发方式，时代在召唤。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;除去前文中讲的一些不变的内容，还有很多内容是一直在变化的，虽然说用老旧的开发方式也是可以开发出能用的 C++ 原生模块，但是旧不如新。&lt;/p&gt;

&lt;p&gt;而且，其实目前来说 node-gyp 的地位也有可能在未来进行变化。因为当年 Chromium 是通过 GYP 来管理它的构建配置的，现如今已经步入了 GN[^6] 的殿堂，是否也意味着 node-gyp 有一天也会被可能叫做 node-gn 的东西给取代呢？&lt;/p&gt;

&lt;p&gt;话不多说，先来看看沧海桑田的故事吧。&lt;/p&gt;
&lt;h3 id="黑暗时代：node-waf"&gt;黑暗时代：node-waf&lt;/h3&gt;
&lt;p&gt;在 Node.js 0.8 之前，通常在开发 C++ 原生模块的时候，是通过 node-waf 构建的。当然彼 node-waf 不是现在在 NPM 仓库上能搜到的 node-waf 了，当年那个 node-waf 早就年久失修了。&lt;/p&gt;

&lt;p&gt;这个东西使用一种叫 wscript 的文件来配置。自 Node.js 升上 0.8 之后，就自带了 node-gyp 的支持，从此就不再需要 wscript 了。&lt;/p&gt;

&lt;p&gt;不过就是因为有这个青黄交接的时候，那段时间的各种使用 C++ 来开发 Node.js 原生扩展的包为了兼容 0.8 前后版本的 Node.js，通常都是 binding.gyp 和 wscript 共存的。&lt;/p&gt;

&lt;p&gt;大家可以来看一下 &lt;strong&gt;node-mysql-libmysqlclient&lt;/strong&gt; 这个包在当年相应时间段的时候的&lt;a href="https://github.com/Sannis/node-mysql-libmysqlclient/tree/9545ea7485fcc8b07b7c56c5ec3575938bfd4e5f" rel="nofollow" target="_blank" title=""&gt;仓库文件&lt;/a&gt;。为了支持 node-gyp，有一个 binding.gyp 文件，然后还存留着 wscript 配置文件。&lt;/p&gt;
&lt;h3 id="封建时代：暴力！暴力！暴力！"&gt;封建时代：暴力！暴力！暴力！&lt;/h3&gt;
&lt;p&gt;在早期的时候，Node.js 原生 C++ 模块开发方式是非常暴力的，直接使用其提供的原生模块开发头文件。&lt;/p&gt;

&lt;p&gt;开发者直接深入到 Node.js 的各种 API，以及 Google V8 的 API。&lt;/p&gt;

&lt;p&gt;举个最简单的例子，在几年前，你的 Node.js C++ 原生扩展代码可能是长这样的。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;HandleScope&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ThrowException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wrong number of arguments."&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Undefined&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NewSymbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;FunctionTemplate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetFunction&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是一个最简单的 &lt;code&gt;echo&lt;/code&gt; 函数，返回传进来的参数。写作 JavaScript 相当于是这样的。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wrong number of arguments.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;遗憾的是，这样的代码如果发成一个包，你现在是无论如何无法安装的，除非你用的是 0.10.x 的 Node.js 版本。&lt;/p&gt;

&lt;p&gt;为什么这么说呢，这段代码的确是在 Node.js 0.10.x 的时候可以用的。但是再往上升 Google V8 的大版本，这段代码就无法适用了，讲粗暴点就是没办法再编译通过了。&lt;/p&gt;

&lt;p&gt;就拿 Node.js 6.x 版本的 Google V8 来说，函数声明的对比是这样的：&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// 0.10.x&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionCallbackInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 6.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;事实上，根本不需要等到 6.x。上面的代码到 0.12 就已经无法再编译通过了。不只是函数声明的变化，连句柄作用域 [^7] 的声明方式都变了。&lt;/p&gt;

&lt;p&gt;如果要让它在 Node.js 6.x 下能编译，就需要改代码，就像这样。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;FunctionCallbackInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Isolate&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;isolate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetIsolate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ThrowException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NewFromUtf8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Wrong number of arguments."&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetReturnValue&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;NODE_SET_METHOD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是说，以黑暗时代的方式进行 Node.js 原生模块开发的时候，一个版本只能支持特定几个版本的 Node.js，一旦 Node.js 的底层 API 以及 Google V8 的 API 发生变化，而这些原生模块又依赖了变化了的 API 的话，包就作废了。除非包的维护者去支持新版的 API，不过这样依赖，老版 Node.js 下就又无法编译通过新版的包了。&lt;/p&gt;

&lt;p&gt;这就很尴尬了。&lt;/p&gt;
&lt;h3 id="城堡时代：Native Abstractions for Node.js"&gt;城堡时代：Native Abstractions for Node.js&lt;/h3&gt;
&lt;p&gt;在经历了黑暗时代的尴尬局面之后，2013 年年中，一个救世主突然现世。&lt;/p&gt;

&lt;p&gt;它的名字叫作 &lt;a href="https://github.com/nodejs/nan" rel="nofollow" target="_blank" title=""&gt;NAN&lt;/a&gt;，全称 Native Abstractions for Node.js，即 Node.js 原生模块抽象接口。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NAN 由 &lt;a href="https://github.com/rvagg" rel="nofollow" target="_blank" title=""&gt;Rod Vagg&lt;/a&gt; 和 &lt;a href="https://github.com/kkoopa" rel="nofollow" target="_blank" title=""&gt;Benjamin Byholm&lt;/a&gt; 两手带大，记名在 GitHub 的 Rod Vagg 账号下。并且在 Node.js 与 io.js 黑历史的年代，这个在 GitHub 上面项目移到了 io.js 的组织下面；后来由于两家又重归于好，NAN 最终归属到了 &lt;a href="https://github.com/nodejs" rel="nofollow" target="_blank" title=""&gt;nodejs&lt;/a&gt; 这个组织下面。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;总之在 NAN 出现之后，Node.js 的原生开发方式进入了城堡时代，并且一直持续到现在，甚至可能会持续到好久之后。&lt;/p&gt;

&lt;p&gt;说 NAN 是 Node.js 原生模块抽象接口可能还是有点抽象，那么讲明白点，它就是一堆宏判断。比如声明一个函数的时候，只需要通过下面的一个宏就可以了：&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;NAN_METHOD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NAN 的宏会判断当前编译时候的 Node.js 版本，根据不同版本的 Node.js 来展开不同的结果。这会儿就又会提到先前的两个函数声明对比了。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// 0.10.x&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionCallbackInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 6.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;NAN_METHOD&lt;/code&gt; 将会在不同版本的 Node.js 下被 NAN 展开成上面两个这样。&lt;/p&gt;

&lt;p&gt;而且 NAN 可不只是提供了 &lt;code&gt;NAN_METHOD&lt;/code&gt; 一个宏，它还有一坨一坨数不清的宏供开发者使用。&lt;/p&gt;

&lt;p&gt;比如声明句柄作用域的 &lt;code&gt;Nan::HandleScope&lt;/code&gt;、能黑盒调起 libuv[^8] 进行事件循环上的异步操作的 &lt;code&gt;Nan::AsyncWorker&lt;/code&gt; 等。&lt;/p&gt;

&lt;p&gt;于是，在城堡时代，大家的 C++ 原生模块代码都差不多长这样。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;NAN_METHOD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ThrowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wrong number of arguments."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetReturnValue&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Undefined&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetReturnValue&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;NAN_MODULE_INIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InitAll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ToLocalChecked&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;v8&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FunctionTemplate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;ToLocalChecked&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样做的好处就是，代码只需要随着 NAN 的升级做改变就好，它会帮你兼容各不同 Node.js 版本，使其在任意版本都能被编译使用。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;即使是 NAN 这样的好物，也有自己的一个使命，使命之外的东西会被逐渐剥离。比如 0.10.x 和 0.12.x 等版本就应该要退出历史舞台了，NAN 会逐渐放弃对它们的兼容和支持。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="帝国时代：符合 ABI 的 N-API"&gt;帝国时代：符合 ABI 的 N-API&lt;/h3&gt;
&lt;p&gt;自从前几天 Node.js v8.0.0 发布之后，Node.js 推出了全新的用于开发 C++ 原生模块的接口，N-API。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;据官方文档所述，它的发音就是一个单独的 &lt;code&gt;N&lt;/code&gt;，加上 API，即四个英文字母单独发音。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这东西相较于先前三个时代有什么不同呢？为什么会是更进一步的帝国时代呢？&lt;/p&gt;

&lt;p&gt;首先，我们知道，即使是在 NAN 的开发方式下，一次编写好的代码在不同版本的 Node.js 下也需要重新编译，否则版本不符的话 Node.js 无法正常载入一个 C++ 扩展。即一次编写，到处编译。&lt;/p&gt;

&lt;p&gt;而 N-API 相较于 NAPI 来说，它把 Node.js 的所有底层数据结构全部黑盒化，抽象成 N-API 当中的接口。&lt;/p&gt;

&lt;p&gt;不同版本的 Node.js 使用同样的接口，这些接口是稳定地 ABI 化的，即应用二进制接口（Application Binary Interface）。这使得在不同 Node.js 下，只要 ABI 的版本号一致，编译好的 C++ 扩展就可以直接使用，而不需要重新编译。事实上，在支持 N-API 接口的 Node.js 中，的确就指定了当前 Node.js 所使用的 ABI 版本。&lt;/p&gt;

&lt;p&gt;为了使得以后的 C++ 扩展开发、维护更方便，N-API 致力于以下的几个目标：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;以 C 的风格提供稳定 ABI 接口；&lt;/li&gt;
&lt;li&gt;消除 Node.js 版本的差异；&lt;/li&gt;
&lt;li&gt;消除 JavaScript 引擎的差异（如 Google V8、Microsoft ChakraCore 等）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而这些 API 主要就是用来创建和操作 JavaScript 的值了，我们就再也不用直接使用 Google V8 提供的数据类型了。毕竟在 NAN 中，就算我们有时候看不到 Google V8 的影子，实际上在宏展开后还是无数的 Google V8 数据结构。&lt;/p&gt;

&lt;p&gt;为了达成上述隐藏的目标，N-API 的姿势就变成了这样：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;提供头文件 &lt;strong&gt;node_api.h&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;任何 N-API 调用都返回一个 &lt;code&gt;napi_status&lt;/code&gt; 枚举，来表示这次调用成功与否；&lt;/li&gt;
&lt;li&gt;N-API 的返回值由于被 &lt;code&gt;napi_status&lt;/code&gt; 占坑了，所以真实返回值由传入的参数来继承，如传入一个指针让函数操作；&lt;/li&gt;
&lt;li&gt;所有 JavaScript 数据类型都被黑盒类型 &lt;code&gt;napi_value&lt;/code&gt; 封装，不再是类似于 &lt;code&gt;v8::Object&lt;/code&gt;、&lt;code&gt;v8::Number&lt;/code&gt; 等类型；&lt;/li&gt;
&lt;li&gt;如果函数调用不成功，可以通过 &lt;code&gt;napi_get_last_error_info&lt;/code&gt; 函数来获取最后一次出错的信息。&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;哪怕是现在的 Node.js v8.x 版本，N-API 仍处于一个实验状态，个人认为还有非常长的一段路要走，所以大家在生产环境中还不必太过于激进，不过 N-API 依然是大势所趋；不过对于使用老版本的 Node.js 开发者来说，大家也不要着急，即使 N-API 是在 v8.x 才正式集成进 Node.js，在其它旧版本的 Node.js 中依然可以将 N-API 作为外挂式的头文件 9 中使用，只不过无法做到跨版本的特性，这只是它做的向后兼容的一个事情而已。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;关于 N-API 一系列的函数可以访问它的&lt;a href="https://nodejs.org/docs/v8.0.0/api/n-api.html" rel="nofollow" target="_blank" title=""&gt;文档&lt;/a&gt;了解更多详情，现在我们来点料儿让大家对 N-API 的印象不是那么抽象。&lt;/p&gt;
&lt;h4 id="模块初始化"&gt;模块初始化&lt;/h4&gt;
&lt;p&gt;在封建时代和 NAN 所处的，模块的初始化是交给 Node.js 提供的宏来实现的。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;NODE_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而到了当前的 N-API，它就变成了 N-API 的一个宏了。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;NAPI_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相应地，这个初始化函数 &lt;code&gt;Init&lt;/code&gt; 的写法也会有所改变。比如这是封建时代和 NAN 时代的两种不同写法：&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 暴力写法&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;NODE_SET_METHOD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// NAN 写法&lt;/span&gt;
&lt;span class="n"&gt;NAN_MODULE_INIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ToLocalChecked&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Nan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;v8&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FunctionTemplate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;ToLocalChecked&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而到了 N-API 的时候，这个 &lt;code&gt;Init&lt;/code&gt; 函数就该是这样的了。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;napi_env&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;napi_status&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 用于设置 exports 对象的描述结构体&lt;/span&gt;
    &lt;span class="n"&gt;napi_property_descriptor&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// 把 "echo" 设置到 exports 去&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;napi_define_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;napi_property_descriptor&lt;/code&gt; 是用于设置对象属性的描述结构体，它的声明如下：&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;utf8name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;napi_callback&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;napi_callback&lt;/span&gt; &lt;span class="n"&gt;getter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;napi_callback&lt;/span&gt; &lt;span class="n"&gt;setter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;napi_property_attributes&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;napi_property_descriptor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么上面 &lt;code&gt;Init&lt;/code&gt; 函数中的 &lt;code&gt;desc&lt;/code&gt; 意思就是，即将被挂在的对象下会挂一个叫 &lt;code&gt;"echo"&lt;/code&gt; 的东西，它的函数是 &lt;code&gt;Echo&lt;/code&gt;，其它的 &lt;code&gt;getter&lt;/code&gt;、&lt;code&gt;setter&lt;/code&gt; 等全是空指针，而属性则是 &lt;code&gt;napi_default&lt;/code&gt;。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;napi_property_attributes&lt;/code&gt; 除了 &lt;code&gt;napi_default&lt;/code&gt; 之外，还有诸如只读、是否可枚举等属性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;h4 id="函数声明"&gt;函数声明&lt;/h4&gt;
&lt;p&gt;还记得之前的两种函数声明吗？第三次再搬过来。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// 0.10.x&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionCallbackInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 6.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 N-API 中，你不用再被告知需要有 C++ 基础，C 即可。因为在 N-API 里面，声明一个 &lt;code&gt;Echo&lt;/code&gt; 是这样的：&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;napi_env&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_callback_info&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;napi_status&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;napi_get_cb_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;napi_ok&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;napi_throw_type_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Wrong number of arguments"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// napi_value 实际上是一个指针，返回空指针表示无返回值&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;重要：&lt;/strong&gt;目前 8.0.0 和 8.1.0 版本的 Node.js 官方文档中，关于 N-API 的各种接口文档错误颇多，所以还是要以能使用的接口为准。&lt;/p&gt;

&lt;p&gt;而且现在大家也有很多人正在帮忙一起修复文档。例如现在的 JavaScript 函数声明返回值其实是 &lt;code&gt;napi_value&lt;/code&gt;，而官方文档上还是老旧的 &lt;code&gt;void&lt;/code&gt;。又比如 `&lt;code&gt;napi_property_descriptor_desc&lt;/code&gt; 结构体中，在 &lt;code&gt;utf8name&lt;/code&gt; 之后还有一个 &lt;code&gt;napi_value&lt;/code&gt; 的变量，而文档中却是没有的。&lt;/p&gt;

&lt;p&gt;这也是为什么我前面强调目前来说 N-API 还处于试验阶段。毕竟 API 并没有完全稳定下来，还处于一个快速迭代的步伐中，文档的更新并未跟上代码的更新。至少在笔者写作的当前是这样的（现在日期 2017 年 6 月 9 日）。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;上面代码分步解析。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;通过 &lt;code&gt;napi_get_cb_info&lt;/code&gt; 获取当次函数请求的参数信息，包括参数数量和参数体（参数体以 &lt;code&gt;napi_value&lt;/code&gt; 的数组形式体现）；&lt;/li&gt;
&lt;li&gt;看看解析有无出错（&lt;code&gt;status&lt;/code&gt; 不等于 &lt;code&gt;napi_ok&lt;/code&gt;）或者看看参数数量是否小于 1；

&lt;ul&gt;
&lt;li&gt;若解析出错或者参数数量小于 1，通过 &lt;code&gt;napi_throw_type_error&lt;/code&gt; 在 JavaScript 层抛出一个错误对象，并返回；&lt;/li&gt;
&lt;li&gt;若无错则继续进行；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;argv[0]&lt;/code&gt;，即第一个参数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="Demo 完整代码"&gt;Demo 完整代码&lt;/h4&gt;
&lt;p&gt;这里放上这个 Echo 样例的完整代码，大家可以拿回家试试看。&lt;/p&gt;
&lt;h5 id="binding.gyp"&gt;binding.gyp&lt;/h5&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"targets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"addon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"addon.cc"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cflags!"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-fno-exceptions"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cflags_cc!"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-fno-exceptions"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"xcode_settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"GCC_ENABLE_CPP_EXCEPTIONS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"CLANG_CXX_LIBRARY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"libc++"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"MACOSX_DEPLOYMENT_TARGET"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10.7"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"msvs_settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"VCCLCompilerTool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ExceptionHandling"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="addon.cc"&gt;addon.cc&lt;/h5&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;node_api.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="nf"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;napi_env&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_callback_info&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;napi_status&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;napi_get_cb_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;napi_ok&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;napi_throw_type_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Wrong number of arguments"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;napi_get_undefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;napi_env&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_value&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;napi_status&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;napi_property_descriptor&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;napi_default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;napi_define_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;NAPI_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="乘风破浪"&gt;乘风破浪&lt;/h4&gt;
&lt;p&gt;在完成了代码之后，大家赶紧试一下代码吧。&lt;/p&gt;

&lt;p&gt;首先在 Node.js v8.x 下进行试验，把这两段代码分别放到同一个目录下，命名好后，执行这样的终端命令：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node-gyp rebuild
...
&lt;span class="nv"&gt;$ &lt;/span&gt;node &lt;span class="nt"&gt;--napi-modules&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;node:52264&lt;span class="o"&gt;)&lt;/span&gt; Warning: N-API is an experimental feature and could change at any &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; const addon &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./build/Release/addon"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
undefined
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; addon.echo&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2333"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="s1"&gt;'2333'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; addon.echo&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"蛋花汤🐶"&lt;/span&gt;, &lt;span class="s2"&gt;"南瓜饼🐱"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="s1"&gt;'蛋花汤🐶'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; addon.echo&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
TypeError: Wrong number of arguments
    at repl:1:7
    at ContextifyScript.Script.runInThisContext &lt;span class="o"&gt;(&lt;/span&gt;vm.js:44:33&lt;span class="o"&gt;)&lt;/span&gt;
    at REPLServer.defaultEval &lt;span class="o"&gt;(&lt;/span&gt;repl.js:239:29&lt;span class="o"&gt;)&lt;/span&gt;
    at bound &lt;span class="o"&gt;(&lt;/span&gt;domain.js:301:14&lt;span class="o"&gt;)&lt;/span&gt;
    at REPLServer.runBound &lt;span class="o"&gt;[&lt;/span&gt;as &lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;domain.js:314:12&lt;span class="o"&gt;)&lt;/span&gt;
    at REPLServer.onLine &lt;span class="o"&gt;(&lt;/span&gt;repl.js:433:10&lt;span class="o"&gt;)&lt;/span&gt;
    at emitOne &lt;span class="o"&gt;(&lt;/span&gt;events.js:120:20&lt;span class="o"&gt;)&lt;/span&gt;
    at REPLServer.emit &lt;span class="o"&gt;(&lt;/span&gt;events.js:210:7&lt;span class="o"&gt;)&lt;/span&gt;
    at REPLServer.Interface._onLine &lt;span class="o"&gt;(&lt;/span&gt;readline.js:278:10&lt;span class="o"&gt;)&lt;/span&gt;
    at REPLServer.Interface._line &lt;span class="o"&gt;(&lt;/span&gt;readline.js:625:8&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;还是因为试验特性，目前在 Node.js v8.x 要加载和执行 N-API 的 C++ 扩展的话，在启动 &lt;code&gt;node&lt;/code&gt; 的时候需要加上 &lt;code&gt;--napi-modules&lt;/code&gt; 参数，表示这次执行要启用 N-API 特性。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;效果显而易见，在刚启动 Node.js REPL 的时候，你会得到一个警告。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(node:52264) Warning: N-API is an experimental feature and could change at any time&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;表示它目前还不是特别稳定，但是值得我们展望未来。然后在我们 &lt;code&gt;require()&lt;/code&gt; 扩展的时候，我们就得到了一个拥有 &lt;code&gt;echo&lt;/code&gt; 函数的对象了。&lt;/p&gt;

&lt;p&gt;我们尝试了三种调用方式。第一次是规规矩矩传入一个参数，&lt;code&gt;echo&lt;/code&gt; 如期返回我们传入的参数 &lt;code&gt;"2333"&lt;/code&gt;；第二次传入两个参数，&lt;code&gt;echo&lt;/code&gt; 返回了第一个参数 &lt;code&gt;"蛋花汤🐶"&lt;/code&gt;；最后一次我们没传任何参数，这个时候就走到了 C++ 扩展中判断函数参数数量失败的条件分支，就抛出了一个 &lt;code&gt;Wrong number of arguments&lt;/code&gt; 的错误对象。&lt;/p&gt;

&lt;p&gt;总之，它按照我们的预期跑起来了。并且代码里面并没有任何 Node.js 非 N-API 所暴露出来的数据结构和 V8 的数据结构——版本差异消除了。&lt;/p&gt;

&lt;p&gt;接下来激动人心的时刻到了，如果读者是使用 &lt;code&gt;nvm&lt;/code&gt; 来管理自己的 Node.js 版本的话，可以尝试着安装一个 8.1.0 的 Node.js 版本。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;8.1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在安装成功切换版本成功后，尝试着直接打开 Node.js RELP，忘掉再次编译刚才编译好的扩展这一步。（不过别忘了 &lt;code&gt;--napi-module&lt;/code&gt; 参数）&lt;/p&gt;

&lt;p&gt;把刚才用于测试的几句 JavaScript 代码再重复地输入——N-API 诚不我欺，居然还是能输出结果。这对于以前的暴力做法和 NAN 做法来说，无疑是非常大的一个进步。&lt;/p&gt;
&lt;h4 id="向下兼容"&gt;向下兼容&lt;/h4&gt;
&lt;p&gt;至此，我希望大家还没有忘记 N-API 是自 Node.js 8.0 之后出的特性。所以之前 Demo 的代码并不能在 Node.js 8.0 之前的版本如期编译和运行。&lt;/p&gt;

&lt;p&gt;辛辛苦苦写好的包，居然不能在 Node.js 6.x 下面跑，搞什么。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://images.gitbook.cn/25ae5fa0-4cf6-11e7-92f5-09e07d17628a" title="" alt="当时我就不乐意了"&gt;&lt;/p&gt;

&lt;p&gt;先别急着摔。文中之前也说了，有一个外挂式头文件的包，其包名是 &lt;code&gt;node-addon-api&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;我们就试着通过它来进行向下兼容吧。首先在我们刚才的源码目录把这个包给安装上。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; node-addon-api
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;还是由于快速迭代的原因，我不能保证这个包当前版本的时效性，不过我相信大家都有探索精神，在未来版本不符导致的 API 不符的问题应该都能解决。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;然后，给我们的 &lt;strong&gt;binding.gyp&lt;/strong&gt; 函数加点料，加两个字段，里面是两个指令展开。&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"include_dirs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;!@(node -p &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;require('node-addon-api').include&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;!(node -p &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;require('node-addon-api').gyp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;!@&lt;/code&gt; 和 &lt;code&gt;&amp;lt;!&lt;/code&gt; 开头的字符串在 GYP 中代表指令，表示它的值是后面的指令的执行结果。上面两条指令的返回结果分别是外挂式头文件的头文件搜索路径，以及外挂式 N-API 这个包编译成静态连接库供我们自己的包使用的依赖声明。&lt;/p&gt;

&lt;p&gt;有了这两个字段后，就表示我们依赖了外挂式 N-API 头文件。而且它内部自带判断，如果版本已经达到了有 N-API 的要求，它的依赖就会是一个空依赖，即不依赖外挂式 N-API 编译的静态连接库。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;也就是说，用了外挂式的 N-API，能自动适配 Node.js 8.x 和低版本。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;于是这个 &lt;strong&gt;binding.gyp&lt;/strong&gt; 现在看起来是这样子的。&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"targets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"addon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"addon.cc"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cflags!"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-fno-exceptions"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cflags_cc!"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-fno-exceptions"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"xcode_settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"GCC_ENABLE_CPP_EXCEPTIONS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"CLANG_CXX_LIBRARY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"libc++"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"MACOSX_DEPLOYMENT_TARGET"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10.7"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"msvs_settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"VCCLCompilerTool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ExceptionHandling"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"include_dirs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;!@(node -p &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;require('node-addon-api').include&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;!(node -p &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;require('node-addon-api').gyp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至于源码层面，我们就不需要作任何修改。在 Node.js v6.x 下面试试看吧。同样是使用 &lt;strong&gt;node-gyp rebuild&lt;/strong&gt; 进行编译。然后通过 Node.js REPL 进去测试。&lt;/p&gt;

&lt;p&gt;具体的终端输出这里就不放出来了，相信经过实验的大家都得到了自己想要的结果。&lt;/p&gt;
&lt;h2 id="小结"&gt;小结&lt;/h2&gt;
&lt;p&gt;本次内容主要讲解了在 Node.js 领域中原生 C++ 模块开发的方式变迁。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;从 node-waf 到 node-gyp，这是构建工具的一个变迁，未来说不定会是 GN 或者其它的构建工具。&lt;/li&gt;
&lt;li&gt;从暴力写码，到 NAN 的出现，见证了 Node.js 社区的各种爱恨情仇，一直到现在的新生儿 N-API，为原生 C++ 模块的开发输送了新鲜的血液。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;目前的中坚力量仍然是 NAN 的开发方式，甚至我猜测是否未来有可能 NAN 会提供关于 N-API 的各种宏封装，使其彻底消除版本差异，包括 ABI 版本上的差异。当然这种 ABI 版本差异导致的需要多次编译问题应该还是存在的，这里指的是一次编码的差异。&lt;/p&gt;

&lt;p&gt;在大家跟着本文对 N-API 进行了一次浅尝辄止的尝试之后，希望能对当下仍然处于实验状态的 N-API 充满了希冀，并对现在存在的各种坑处以包容的心态。&lt;/p&gt;

&lt;p&gt;毕竟，Node.js loves you all。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;「Consider moving from gyp to gn」：&lt;a href="https://github.com/nodejs/node/issues/6089" rel="nofollow" target="_blank"&gt;https://github.com/nodejs/node/issues/6089&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;「Getting Started with Embedding · v8/v8 Wiki」：&lt;a href="https://github.com/v8/v8/wiki/Getting-Started-with-Embedding" rel="nofollow" target="_blank"&gt;https://github.com/v8/v8/wiki/Getting-Started-with-Embedding&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;「Drop support for v0.10 and v0.12?」：&lt;a href="https://github.com/nodejs/nan/issues/676" rel="nofollow" target="_blank"&gt;https://github.com/nodejs/nan/issues/676&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;「Node Loves Rust」：&lt;a href="https://cnodejs.org/topic/593353775b07c1b24afa0638" rel="nofollow" target="_blank"&gt;https://cnodejs.org/topic/593353775b07c1b24afa0638&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;「N-API | Node.js v8.0.0 Documentation」：&lt;a href="https://nodejs.org/docs/v8.0.0/api/n-api.html" rel="nofollow" target="_blank"&gt;https://nodejs.org/docs/v8.0.0/api/n-api.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;「doc: fix out of date sections in n-api doc」：&lt;a href="https://github.com/nodejs/node/pull/13508" rel="nofollow" target="_blank"&gt;https://github.com/nodejs/node/pull/13508&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;[^1]: 用于定义某种文件类型的特殊标识，详见 &lt;a href="https://en.wikipedia.org/wiki/Magic_number_programming)" rel="nofollow" target="_blank"&gt;https://en.wikipedia.org/wiki/Magic_number_programming)&lt;/a&gt;(
[^2]: 代码参见 &lt;a href="https://github.com/nodejs/node/blob/v6.9.4/src/node.cc#L2427-L2502" rel="nofollow" target="_blank"&gt;https://github.com/nodejs/node/blob/v6.9.4/src/node.cc#L2427-L2502&lt;/a&gt;
[^3]: 年久失修，当前 NPM 上搜索到的 node-waf 已经不是当年的了，不过这个是 Waf 的官方仓库 &lt;a href="https://github.com/waf-project/waf" rel="nofollow" target="_blank"&gt;https://github.com/waf-project/waf&lt;/a&gt;。
[^4]: 全称 Generate Your Projects，是谷歌开发的一套构建系统，未尽事宜详询 &lt;a href="https://gyp.gsrc.io" rel="nofollow" target="_blank"&gt;https://gyp.gsrc.io&lt;/a&gt;。
[^5]: GYP 的配置文件的后缀就是 *.gyp 或者 *.gypi 等，是个类 JSON 文件。
[^6]: GN 是谷歌开发的相较于 GYP 更新更快的一套构建工具，可以参考 &lt;a href="https://chromium.googlesource.com/chromium/src/tools/gn/+/HEAD/docs/quick_start.md" rel="nofollow" target="_blank"&gt;https://chromium.googlesource.com/chromium/src/tools/gn/+/HEAD/docs/quick_start.md&lt;/a&gt;
[^7]: 让垃圾回收机制来管理 JavaScript 对象生命周期的一种类，即 HandleScope，在我的新书中将会有详解。
[^8]: Node.js 的异步事件循环支撑者，详询 &lt;a href="http://www.libuv.org/" rel="nofollow" target="_blank"&gt;http://www.libuv.org/&lt;/a&gt;
[^9]: 详情请查看 node-api 这个包，&lt;a href="https://github.com/nodejs/node-api" rel="nofollow" target="_blank"&gt;https://github.com/nodejs/node-api&lt;/a&gt;&lt;/p&gt;</description>
      <author>xadillax</author>
      <pubDate>Sat, 01 Jul 2017 17:05:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/33378</link>
      <guid>https://ruby-china.org/topics/33378</guid>
    </item>
    <item>
      <title>开场 Live，分享点干货——「深入了解 Node.js 包与模块机制」</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;先放上 Live 地址： &lt;a href="https://www.zhihu.com/lives/842742839304667136" rel="nofollow" target="_blank"&gt;https://www.zhihu.com/lives/842742839304667136&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本次 Live 将深入剖析 Node.js 包与模块机制，包括且不限于解析 Node.js 源码、社区规范等。本人认为这是作为一个合格 Node.js 开发者哪怕是不深入也要了解的姿势之一。&lt;/p&gt;

&lt;p&gt;本次 Live 主要包括以下内容：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CommonJs 模块规范&lt;/li&gt;
&lt;li&gt;CommonJs 包规范&lt;/li&gt;
&lt;li&gt;Node.js 模块规范&lt;/li&gt;
&lt;li&gt;Node.js 包规范&lt;/li&gt;
&lt;li&gt;Node.js 四种模块本质和加载原理&lt;/li&gt;
&lt;li&gt;Node.js 入口模块&lt;/li&gt;
&lt;li&gt;最后带一笔 Node.js 包（如包管理器，通样等）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然在 Live 过程中，可能略有出入。&lt;/p&gt;</description>
      <author>xadillax</author>
      <pubDate>Wed, 03 May 2017 18:06:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/32924</link>
      <guid>https://ruby-china.org/topics/32924</guid>
    </item>
    <item>
      <title>Byakuren：一个 C 实现的主题色提取库</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;原文地址： &lt;a href="https://xcoder.in/2017/03/03/byakuren/" rel="nofollow" target="_blank"&gt;https://xcoder.in/2017/03/03/byakuren/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好久没发文章了，借着 Byakuren 开源的档口炒炒冷饭吧。&lt;/p&gt;

&lt;p&gt;先上 Repo 地址：&lt;a href="https://github.com/XadillaX/byakuren" rel="nofollow" target="_blank" title=""&gt;https://github.com/XadillaX/byakuren&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="主题色"&gt;主题色&lt;/h2&gt;
&lt;p&gt;图像主题色是从一张图像中提取出来最能代表这张图片主色调的多种颜色。也就是说在一幅色彩斑斓的图片里面，各种不同颜色的数量就对应着该颜色在图 片中的比例，程序可以通过计算图片中不同颜色的像素数来算出主题色。&lt;/p&gt;
&lt;h3 id="主题色提取算法"&gt;主题色提取算法&lt;/h3&gt;
&lt;p&gt;提取的算法在我之前的博客中有说明。在 Byakuren 中其实用的就是之前博客中讲的一些算法。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://xcoder.in/2014/09/17/theme-color-extract/#%E5%85%AB%E5%8F%89%E6%A0%91%E6%8F%90%E5%8F%96%E6%B3%95" title=""&gt;八叉树提取法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://xcoder.in/2014/09/17/theme-color-extract/#%E6%9C%80%E5%B0%8F%E5%B7%AE%E5%80%BC%E6%B3%95" title=""&gt;最小差值法&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;除去上面两种算法，Byakuren 还提供了将这两种算法结合起来的 Mix 算法。&lt;/p&gt;
&lt;h2 id="Byakuren"&gt;Byakuren&lt;/h2&gt;
&lt;p&gt;Byakuren 是我前两年写的一个主题色提取库，也是继 &lt;a href="https://github.com/XadillaX/thmclrx/tree/e302b61b4cc70b24efa35c612899cc0b79d271f9" rel="nofollow" target="_blank" title=""&gt;thmclrx&lt;/a&gt; 之后的 C 版实现，个人认为代码质量比旧版的 thmclrx 要高。并且它实际上经过了企业级的验证，在某相关的公司已经欢快跑了有些年头了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;在经过相关人员的同意下，我也算把这雪藏了好久的代码给开源出来了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="伪·Byakuren 是什么？"&gt;伪·Byakuren 是什么？&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://xcoder.in/2017/03/03/byakuren/byakuren.png" title="" alt="byakuren"&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;聖 白蓮（ひじり びゃくれん，Hiziri Byakuren）是系列作品《东方 project》中的角色，首次登场于《东方星莲船》。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;种族：魔法使&lt;/li&gt;
&lt;li&gt;能力：使用魔法程度的能力（酣畅增强身体能力的）&lt;/li&gt;
&lt;li&gt;危险度：不明&lt;/li&gt;
&lt;li&gt;人类友好度：中&lt;/li&gt;
&lt;li&gt;主要活动场所：命莲寺之类&lt;/li&gt;
&lt;li&gt;命莲寺的住持。虽然原本是人类，不过由于常年的修行已经完全超越了人类。现在已经属于人们常说的魔法使了。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;虽然已经入了佛门，但是不知道什么原因却被妖怪敬仰着。她从来没有像童话故事中的魔法使那样，念诵着咒语治退妖怪。使用的力量完全是邪恶的，一点都不像是圣人，虽然并没有人目击到她与人类为敌，但其实已彻底成为妖怪的同伴了。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好吧，总之本人是个东方控，所以基本上项目名都跟东方有关。&lt;/p&gt;
&lt;h3 id="真·Byakuren 是什么？"&gt;真·Byakuren 是什么？&lt;/h3&gt;
&lt;p&gt;如文章题目所说，它是一个 C 实现的开源主题色提取的库，大家可以把它编译成链接库使用。&lt;/p&gt;

&lt;p&gt;不过目前暂时只支持 Makefile 的形式来编译，大家如果有兴趣也可以自己建个 Windows 下的 Visual Studio 项目等，也欢迎提类似于 CMake 之类的 PR。&lt;/p&gt;
&lt;h3 id="使用"&gt;使用&lt;/h3&gt;
&lt;p&gt;其实详细的使用方法在&lt;a href="https://github.com/XadillaX/byakuren#byakuren" rel="nofollow" target="_blank" title=""&gt;文档&lt;/a&gt;中就有说明。&lt;/p&gt;

&lt;p&gt;不过这里还是简单介绍一下吧。&lt;/p&gt;
&lt;h4 id="Clone"&gt;Clone&lt;/h4&gt;
&lt;p&gt;先把代码给下下来，你也可以把它 Clone 下来。总之仓库地址是：&lt;a href="https://github.com/XadillaX/byakuren" rel="nofollow" target="_blank" title=""&gt;https://github.com/XadillaX/byakuren&lt;/a&gt;。&lt;/p&gt;
&lt;h4 id="make"&gt;make&lt;/h4&gt;
&lt;p&gt;然后跑到目录下执行 &lt;code&gt;make&lt;/code&gt;。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make byakuren
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你将得到一个 &lt;code&gt;byakuren.a&lt;/code&gt; 的静态链接库。&lt;/p&gt;

&lt;p&gt;这个时候你只要拿着这个静态链接库，然后顺便在你的项目中把头文件引进来就可以了。&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"byakuren.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="API"&gt;API&lt;/h4&gt;
&lt;p&gt;我们假设你有 &lt;code&gt;bkr_rgb* rgb&lt;/code&gt; 的图片像素信息，以及 &lt;code&gt;uint32_t color_count&lt;/code&gt; 的图片像素总数两个变量，下面分别给出三个样例。具体的 API 解析还请去文档观摩。&lt;/p&gt;
&lt;h5 id="Octree"&gt;Octree&lt;/h5&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bkr_color_stats&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;bkr_octree_node&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bkr_build_octree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bkr_octree_calculate_color_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bkr_release_octree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;colors&lt;/code&gt; 就是主题色数量了，传进去的 &lt;code&gt;stats&lt;/code&gt; 就会接收到主题色的具体信息了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id="Min-diff"&gt;Min-diff&lt;/h5&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bkr_color_stats&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;bkr_mindiff_parameter&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gray_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;palette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bkr_mindiff_calculate_color_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;colors&lt;/code&gt; 和 &lt;code&gt;stats&lt;/code&gt; 如上所述。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id="Mix"&gt;Mix&lt;/h5&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bkr_color_stats&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;bkr_mindiff_parameter&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gray_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;palette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bkr_mix_calculate_color_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;colors&lt;/code&gt; 和 &lt;code&gt;stats&lt;/code&gt; 如上所述。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="预览一下"&gt;预览一下&lt;/h3&gt;
&lt;p&gt;可能有人想看看效果，我这里就放个效果图给大家看看吧。&lt;/p&gt;

&lt;p&gt;其中 Octree 和 Mix 两个算法的主题色最大数量参数传的都是 16。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://xcoder.in/2017/03/03/byakuren/result.png" title="" alt="效果预览"&gt;&lt;/p&gt;
&lt;h3 id="能做什么？"&gt;能做什么？&lt;/h3&gt;
&lt;p&gt;你可以拿它来写一些主题色提取的东西。&lt;/p&gt;

&lt;p&gt;你也可以拿它来完成其它语言的主题色提取库的封装，如 Python、Lua 等等。欢迎反馈给我。&lt;/p&gt;
&lt;h2 id="结束语"&gt;结束语&lt;/h2&gt;
&lt;p&gt;把一份自己觉得还不错的代码开源出来的感觉特爽，尤其是这种重见天日的感觉。ﾍ|･∀･|ﾉ*~●&lt;/p&gt;</description>
      <author>xadillax</author>
      <pubDate>Fri, 03 Mar 2017 19:08:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/32437</link>
      <guid>https://ruby-china.org/topics/32437</guid>
    </item>
    <item>
      <title>跟我一起部署和定制 CNPM——基础部署</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;原文链接：&lt;a href="https://xcoder.in/2016/07/09/lets-cnpm-base-deploy/" rel="nofollow" target="_blank"&gt;https://xcoder.in/2016/07/09/lets-cnpm-base-deploy/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本章会介绍 CNPM 的基础部署方法。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;该文章所对应的 cnpm 目标版本为 &lt;a href="https://github.com/cnpm/cnpmjs.org/tree/2.12.2" rel="nofollow" target="_blank" title=""&gt;v2.12.2&lt;/a&gt;，上下浮动一些兼容的版本问题也都不是特别大。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="准备"&gt;准备&lt;/h2&gt;
&lt;p&gt;想要部署 CNPM，你需要做以下的一些准备。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;部署的宿体，如服务器、云主机、自己的电脑等；&lt;/li&gt;
&lt;li&gt;数据库，支持 MySQL、PostgreSQL、MariaDB，如果使用 SQLite 则无需准备；&lt;/li&gt;
&lt;li&gt;Git 客户端（推荐）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="开始部署"&gt;开始部署&lt;/h2&gt;&lt;h3 id="克隆 CNPM"&gt;克隆 CNPM&lt;/h3&gt;
&lt;p&gt;首先在本地选择一个目录，比如我将它选择在 &lt;code&gt;/usr/app&lt;/code&gt;，然后预想 CNPM 的目录为 &lt;code&gt;/usr/app/cnpm&lt;/code&gt;，那么需要在终端 &lt;code&gt;$ cd /usr/app&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;接下去执行 Git 指令将 CNPM 克隆到相应目录。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/cnpm/cnpmjs.org.git
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="Windows 用户"&gt;Windows 用户&lt;/h4&gt;
&lt;p&gt;Windows 用户也可以用类似 &lt;a href="https://www.cygwin.com/" rel="nofollow" target="_blank" title=""&gt;Cygwin&lt;/a&gt;、&lt;a href="http://www.mingw.org/" rel="nofollow" target="_blank" title=""&gt;MinGW&lt;/a&gt;、&lt;a href="https://msdn.microsoft.com/en-us/powershell" rel="nofollow" target="_blank" title=""&gt;Powershell&lt;/a&gt; 甚至直接是 Command 等来运行 &lt;a href="https://git-scm.com/download/win" rel="nofollow" target="_blank" title=""&gt;Git&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;当然也可以直接下载一些 GUI 工具来克隆，如 &lt;a href="https://www.sourcetreeapp.com/" rel="nofollow" target="_blank" title=""&gt;SourceTree&lt;/a&gt;。&lt;/p&gt;
&lt;h4 id="非 Git 用户"&gt;非 Git 用户&lt;/h4&gt;
&lt;p&gt;跑到 CNPM 的 Release 页面，选择相应的版本下载，比如这里会选择 &lt;a href="https://github.com/cnpm/cnpmjs.org/releases/tag/2.12.2" rel="nofollow" target="_blank" title=""&gt;v2.12.2&lt;/a&gt; 版。&lt;/p&gt;

&lt;p&gt;下载完毕后将文件夹解压到相应目录即可。&lt;/p&gt;
&lt;h3 id="安装依赖"&gt;安装依赖&lt;/h3&gt;
&lt;p&gt;安装依赖其实就是一个 &lt;code&gt;npm install&lt;/code&gt;，不过 CNPM 把该指令已经写到 Makefile 里面了，所以直接执行下面的命令就好了。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然万一你是 Windows 用户或者不会 &lt;code&gt;make&lt;/code&gt;，那么还是要用 &lt;code&gt;npm install&lt;/code&gt;。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--build-from-source&lt;/span&gt; &lt;span class="nt"&gt;--registry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://registry.npm.taobao.org &lt;span class="nt"&gt;--disturl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://npm.taobao.org/mirrors/node
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="修改配置文件"&gt;修改配置文件&lt;/h3&gt;
&lt;p&gt;新建一份 &lt;code&gt;config/config.js&lt;/code&gt; 文件，并且写入如下的骨架：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里面输入你需要的键值对。&lt;/p&gt;

&lt;p&gt;这里将会列举一些常用的配置项，其余的一些配置项请自行参考 &lt;a href="https://github.com/cnpm/cnpmjs.org/blob/2.12.2/config/index.js" rel="nofollow" target="_blank" title=""&gt;config/index.js&lt;/a&gt; 文件。&lt;/p&gt;
&lt;h4 id="配置字段参考"&gt;配置字段参考&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;enableCluster&lt;/code&gt;：是否启用 &lt;strong&gt;cluster-worker&lt;/strong&gt; 模式启动服务，默认 &lt;code&gt;false&lt;/code&gt;，生产环节推荐为 &lt;code&gt;true&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;registryPort&lt;/code&gt;：API 专用的 registry 服务端口，默认 &lt;code&gt;7001&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;webPort&lt;/code&gt;：Web 服务端口，默认 &lt;code&gt;7002&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bindingHost&lt;/code&gt;：监听绑定的 Host，默认为 &lt;code&gt;127.0.0.1&lt;/code&gt;，如果外面架了一层本地的 &lt;strong&gt;Nginx&lt;/strong&gt; 反向代理或者 &lt;strong&gt;Apache&lt;/strong&gt; 反向代理的话推荐不用改；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sessionSecret&lt;/code&gt;：&lt;strong&gt;session&lt;/strong&gt; 用的盐；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logdir&lt;/code&gt;：日志目录；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uploadDir&lt;/code&gt;：临时上传文件目录；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;viewCache&lt;/code&gt;：视图模板缓存是否开启，默认为 &lt;code&gt;false&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enableCompress&lt;/code&gt;：是否开启 &lt;strong&gt;gzip&lt;/strong&gt; 压缩，默认为 &lt;code&gt;false&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;admins&lt;/code&gt;：管理员们，这是一个 &lt;code&gt;JSON Object&lt;/code&gt;，对应各键名为各管理员的用户名，键值为其邮箱，默认为 &lt;code&gt;{ fengmk2: 'fengmk2@gmail.com', admin: 'admin@cnpmjs.org', dead_horse: 'dead_horse@qq.com' }&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logoURL&lt;/code&gt;：&lt;strong&gt;Logo&lt;/strong&gt; 地址，不过对于我这个已经把 CNPM 前端改得面目全非的人来说已经忽略了这个配置了；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adBanner&lt;/code&gt;：广告 Banner 的地址；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;customReadmeFile&lt;/code&gt;：实际上我们看到的 &lt;a href="http://cnpmjs.org" rel="nofollow" target="_blank" title=""&gt;cnpmjs.org&lt;/a&gt; 首页中间一大堆冗长的介绍是一个 Markdown 文件转化而成的，你可以设置该项来自行替换这个文件；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;customFooter&lt;/code&gt;：自定义页脚模板；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npmClientName&lt;/code&gt;：默认为 &lt;code&gt;cnpm&lt;/code&gt;，如果你有自己开发或者 fork 的 npm 客户端的话请改成自己的 CLI 命令，这个应该会在一些页面的说明处替换成你所写的；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;backupFilePrefix&lt;/code&gt;：备份目录；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;database&lt;/code&gt;：数据库相关配置，为一个对象，默认如果不配置将会是一个 &lt;code&gt;~/.cnpmjs.org/data.sqlite&lt;/code&gt; 的 SQLite；

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;db&lt;/code&gt;：数据的库名；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt;：数据库用户名；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password&lt;/code&gt;：数据库密码；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dialect&lt;/code&gt;：数据库适配器，可选 &lt;code&gt;"mysql"&lt;/code&gt;、&lt;code&gt;"sqlite"&lt;/code&gt;、&lt;code&gt;"postgres"&lt;/code&gt;、&lt;code&gt;"mariadb"&lt;/code&gt;，默认为 &lt;code&gt;"sqlite"&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hsot&lt;/code&gt;：数据库地址；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;port&lt;/code&gt;：数据库端口；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pool&lt;/code&gt;：数据库连接池相关配置，为一个对象；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxConnections&lt;/code&gt;：最大连接数，默认为 &lt;code&gt;10&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minConnections&lt;/code&gt;：最小连接数，默认为 &lt;code&gt;0&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxIdleTime&lt;/code&gt;：单条链接最大空闲时间，默认为 &lt;code&gt;30000&lt;/code&gt; 毫秒；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;storege&lt;/code&gt;：仅对 SQLite 配置有效，数据库地址，默认为 &lt;code&gt;~/.cnpmjs/data.sqlite&lt;/code&gt;；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nfs&lt;/code&gt;：包文件系统处理对象，为一个 Node.js 对象，默认是 &lt;a href="" title=""&gt;fs-cnpm&lt;/a&gt; 这个包，并且配置在 &lt;code&gt;~/.cnpmjs/nfs&lt;/code&gt; 目录下，也就是说默认所有同步的包都会被放在这个目录下；开发者可以使用别的一些文件系统插件（如上传到又拍云等）,又或者自己去按接口开发一个逻辑层，这些都是后话了；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;registryHost&lt;/code&gt;：暂时还未试过，我猜是用于 Web 页面显示用的，默认为 &lt;code&gt;r.cnpmjs.org&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enablePrivate&lt;/code&gt;：是否开启私有模式，默认为 &lt;code&gt;false&lt;/code&gt;；

&lt;ul&gt;
&lt;li&gt;如果是私有模式则只有管理员能发布包，其它人只能从源站同步包；&lt;/li&gt;
&lt;li&gt;如果是非私有模式则所有登录用户都能发布包；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scopes&lt;/code&gt;：非管理员发布包的时候只能用以 &lt;code&gt;scopes&lt;/code&gt; 里面列举的命名空间为前缀来发布，如果没设置则无法发布，也就是说这是一个必填项，默认为 &lt;code&gt;[ '@cnpm', '@cnpmtest', '@cnpm-test' ]&lt;/code&gt;，据苏千大大解释是为了便于管理以及让公司的员工自觉按需发布；更多关于 NPM scope 的说明请参见 &lt;a href="https://docs.npmjs.com/misc/scope" rel="nofollow" target="_blank" title=""&gt;npm-scope&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;privatePackages&lt;/code&gt;：就如该配置项的注释所述，出于历史包袱的原因，有些已经存在的私有包（可能之前是用 Git 的方式安装的）并没有以命名空间的形式来命名，而这种包本来是无法上传到 CNPM 的，这个配置项数组就是用来加这些例外白名单的，默认为一个空数组；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sourceNpmRegistry&lt;/code&gt;：更新源 NPM 的 registry 地址，默认为 &lt;code&gt;https://registry.npm.taobao.org&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sourceNpmRegistryIsCNpm&lt;/code&gt;：源 registry 是否为 CNPM，默认为 &lt;code&gt;true&lt;/code&gt;，如果你使用的源是官方 NPM 源，请将其设为 &lt;code&gt;false&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syncByInstall&lt;/code&gt;：如果安装包的时候发现包不存在，则尝试从更新源同步，默认为 &lt;code&gt;true&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syncModel&lt;/code&gt;：更新模式（不过我觉得是个 &lt;code&gt;typo&lt;/code&gt;），有下面几种模式可以选择，默认为 &lt;code&gt;"none"&lt;/code&gt;;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"none"&lt;/code&gt;：永不同步，只管理私有用户上传的包，其它源包会直接从源站获取；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"exist"&lt;/code&gt;：定时同步已经存在于数据库的包；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"all"&lt;/code&gt;：定时同步所有源站的包；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syncInterval&lt;/code&gt;：同步间隔，默认为 &lt;code&gt;"10m"&lt;/code&gt; 即十分钟；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syncDevDependencies&lt;/code&gt;：是否同步每个包里面的 &lt;code&gt;devDependencies&lt;/code&gt; 包们，默认为 &lt;code&gt;false&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;badgeSubject&lt;/code&gt;：包的 &lt;strong&gt;badge&lt;/strong&gt; 显示的名字，默认为 &lt;code&gt;cnpm&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;userService&lt;/code&gt;：用户验证接口，默认为 &lt;code&gt;null&lt;/code&gt;，即无用户相关功能也就是无法有用户去上传包，该部分需要自己实现接口功能并配置，如与公司的 &lt;strong&gt;Gitlab&lt;/strong&gt; 相对接，这也是后话了；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alwaysAuth&lt;/code&gt;：是否始终需要用户验证，即便是 &lt;code&gt;$ cnpm install&lt;/code&gt; 等命令；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;httpProxy&lt;/code&gt;：代理地址设置，用于你在墙内源站在墙外的情况。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="一个可能的配置"&gt;一个可能的配置&lt;/h4&gt;
&lt;p&gt;下面给出一个样例配置：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enableCluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;snpm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mysql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;enablePrivate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;admins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;xadillax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;i@2333.moe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;syncModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;nfs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upyun-cnpm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your bucket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;oprator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cheniu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@souche&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@souche-f2e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;badgeSubject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;snpm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;privatePackages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;snpm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;上面的配置包文件系统层用的是 &lt;a href="https://github.com/cnpm/upyun-cnpm" rel="nofollow" target="_blank" title=""&gt;upyun-cnpm&lt;/a&gt; 插件，需要在 CNPM 源码根目录执行&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; upyun-cnpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个时候你的 &lt;code&gt;package.json&lt;/code&gt; 就有更改与源 Repo 不一致了，如果是 Git 克隆的用户在以后升级更新系统的时候稍稍注意一下可能的冲突即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="官方 NFS 插件"&gt;官方 NFS 插件&lt;/h4&gt;
&lt;p&gt;下面给出几个官方的 NFS 插件：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/cnpm/upyun-cnpm" rel="nofollow" target="_blank" title=""&gt;upyun-cnpm&lt;/a&gt;：包本体存在又拍云的插件；&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cnpm/fs-cnpm" rel="nofollow" target="_blank" title=""&gt;fs-cnpm&lt;/a&gt;：包本体存在本地的插件；&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cnpm/sfs-client" rel="nofollow" target="_blank" title=""&gt;sfs-client&lt;/a&gt;：包本体存在 &lt;a href="https://github.com/cnpm/sfs" rel="nofollow" target="_blank" title=""&gt;SFS&lt;/a&gt;（Simple FIle Store）插件；&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cnpm/qn-cnpm" rel="nofollow" target="_blank" title=""&gt;qn-cnpm&lt;/a&gt;：包本体存在七牛的插件；&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cnpm/oss-cnpm" rel="nofollow" target="_blank" title=""&gt;oss-cnpm&lt;/a&gt;：包本体存在阿里云 OSS 的插件。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以后官方如果有一些新的插件进来，这里可能不会更新了，请自行去 &lt;a href="https://github.com/cnpm/cnpmjs.org/wiki/NFS-Guide#present-storage-wrappers" rel="nofollow" target="_blank" title=""&gt;NFS Storage Wrappers&lt;/a&gt; 获取最新的 NFS 插件们。&lt;/p&gt;
&lt;h3 id="初始化数据库"&gt;初始化数据库&lt;/h3&gt;
&lt;p&gt;如果你使用的是 SQLite 的话，数据库是自动就好了的，可以忽略该步。&lt;/p&gt;

&lt;p&gt;其它数据库需要自行导入初始数据库结构。&lt;/p&gt;

&lt;p&gt;初始数据库脚本在 &lt;strong&gt;docs/db.sql&lt;/strong&gt; 里面，你可以用一些 GUI 工具将数据导入，也可以直接进入命令行导入。&lt;/p&gt;

&lt;p&gt;比如你用的是 MySQL，就可以在本机操作 MySQL。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; yourname &lt;span class="nt"&gt;-p&lt;/span&gt;
mysql&amp;gt; use cnpmjs&lt;span class="p"&gt;;&lt;/span&gt;
mysql&amp;gt; &lt;span class="nb"&gt;source &lt;/span&gt;docs/db.sql
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="启动服务"&gt;启动服务&lt;/h3&gt;
&lt;p&gt;搞好配置之后就可以直接启动服务了。&lt;/p&gt;
&lt;h4 id="简单启动"&gt;简单启动&lt;/h4&gt;
&lt;p&gt;最简单的办法也是我现在正在用的方法就是直接用 &lt;code&gt;node&lt;/code&gt; 执行一下入口文件就好了。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node dispatch.js
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;其实我是在 &lt;a href="https://tmux.github.io/" rel="nofollow" target="_blank" title=""&gt;tmux&lt;/a&gt; 里面执行上面的指令的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="官方脚本启动"&gt;官方脚本启动&lt;/h4&gt;
&lt;p&gt;官方的其它一些指令，比如你可以用 NPM 的 script 来运行。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;在 CNPM 里面，npm script 还有下面几种指令&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm run dev&lt;/code&gt;：调试模式启动；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run test&lt;/code&gt;：跑测试；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run start&lt;/code&gt;：启动 CNPM；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run status&lt;/code&gt;：查看 CNPM 启动状态；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run stop&lt;/code&gt;：停止 CNPM。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;</description>
      <author>xadillax</author>
      <pubDate>Tue, 12 Jul 2016 20:10:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/30509</link>
      <guid>https://ruby-china.org/topics/30509</guid>
    </item>
    <item>
      <title>让我们一起来起花名吧</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://xcoder.in/2016/02/24/lets-hua/" rel="nofollow" target="_blank"&gt;https://xcoder.in/2016/02/24/lets-hua/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="起因"&gt;起因&lt;/h2&gt;
&lt;p&gt;起因是我一个叫『小龙』的好基友由于某些原因离职去了一家跟阿里一样有着『花名文化』的公司，于是开始为花名犯愁。&lt;/p&gt;

&lt;p&gt;结合之前妹纸『弍纾』在起花名的时候也遇到了同样的困扰，于是决定用 Node.js 写个『一本正经乱起花名』的程序。&lt;/p&gt;
&lt;h2 id="准备"&gt;准备&lt;/h2&gt;&lt;h3 id="Chinese Random Name"&gt;Chinese Random Name&lt;/h3&gt;
&lt;p&gt;首先起花名的原理就是胡乱随机一串字出来胡乱拼。&lt;/p&gt;

&lt;p&gt;于是准备应该有 &lt;a href="https://github.com/XadillaX/chinese-random-name" rel="nofollow" target="_blank" title=""&gt;chinese-random-name&lt;/a&gt;，一个随机生成中文名的包。&lt;/p&gt;

&lt;p&gt;使用它很简单，先把它 &lt;code&gt;require&lt;/code&gt; 进来：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;randomName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chinese-random-name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="基本用法"&gt;基本用法&lt;/h4&gt;
&lt;p&gt;如果你需要随机生成一个名字只需要 &lt;code&gt;randomName.generate()&lt;/code&gt; 就可以了；如果你要随机一个姓那么就 &lt;code&gt;randomName.surnames.getOne()&lt;/code&gt;；如果你只需要获得名字，这里面就有点门道了。&lt;/p&gt;
&lt;h4 id="高级用法"&gt;高级用法&lt;/h4&gt;
&lt;p&gt;你可以随机生成一个名（不带姓的） &lt;code&gt;randomName.names.get()&lt;/code&gt;；你也可以指定名字的字数，一二三：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;randomName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;randomName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;randomName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者！&lt;/p&gt;

&lt;p&gt;又或者！&lt;/p&gt;

&lt;p&gt;然后或者！&lt;/p&gt;

&lt;p&gt;你可以指定每个字的五行哦！&lt;/p&gt;

&lt;p&gt;什么意思呢？比如你想给孩子起个名，然后孩子命里五行缺金，那么就可以：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;randomName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;金金&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后你就可能得到一个『&lt;strong&gt;紫铨&lt;/strong&gt;』，两个字都是属金的。那么如果你孩子姓李，就叫李紫铨；如果孩子姓王，就叫王紫铨；如果姓爱新觉罗，那么就叫爱新觉罗·紫铨。&lt;/p&gt;

&lt;p&gt;有木有想给我装得这个逼打个 82 分呢？剩下的就交给 666 吧。(ง •̀_•́)ง&lt;/p&gt;
&lt;h3 id="Nomnom"&gt;Nomnom&lt;/h3&gt;
&lt;p&gt;这个包是用来解析命令行参数的。虽然市面上有挺多别的的，比如 &lt;a href="https://www.npmjs.com/package/commander" rel="nofollow" target="_blank" title=""&gt;commander&lt;/a&gt; 等，不过我还是最习惯 &lt;a href="https://www.npmjs.com/package/nomnom" rel="nofollow" target="_blank" title=""&gt;nomnom&lt;/a&gt;，用称手了就不想换了。&lt;/p&gt;

&lt;p&gt;虽然它的 GitHub Repo 下面有这么一段话。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nomnom is deprecated. Check out &lt;a href="https://github.com/tj/commander.js" rel="nofollow" target="_blank"&gt;https://github.com/tj/commander.js&lt;/a&gt;, which should have most, if not all of the capability that nomnom had. Thank you!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;不过再怎么说 nomnom 也是当年 &lt;a href="https://github.com/substack/node-optimist" rel="nofollow" target="_blank" title=""&gt;substack&lt;/a&gt; 大神推荐的啊。(ಡωಡ)&lt;/p&gt;
&lt;h3 id="Colorful"&gt;Colorful&lt;/h3&gt;
&lt;p&gt;这个包是用来上色的。&lt;/p&gt;

&lt;p&gt;毕竟五行是有颜色的哇。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;colorful&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;red&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;（*/∇＼*）&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么在你的终端就好看到一个红色的 &lt;code&gt;（*/∇＼*）&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="Is Chinese"&gt;Is Chinese&lt;/h3&gt;
&lt;p&gt;用来判断是不是中文的包。&lt;/p&gt;

&lt;p&gt;作为一个起名的命令行程序，你总得好好传参才行吧，总不能你随便传个咸鸭蛋🐣我也好好处理吧。&lt;/p&gt;

&lt;p&gt;于是就用 &lt;a href="https://www.npmjs.com/package/is-chinese" rel="nofollow" target="_blank" title=""&gt;is-chinese&lt;/a&gt; 来判断某个字符串是不是纯中文。&lt;/p&gt;

&lt;p&gt;这个包是由前阿里小伙伴，CNode 站长&lt;a href="https://github.com/alsotang" rel="nofollow" target="_blank" title=""&gt;唐少&lt;/a&gt;写的。&lt;/p&gt;

&lt;p&gt;用起来也很简单，只要 &lt;code&gt;isChinese("什么你要判断什么")&lt;/code&gt; 就可以了。&lt;/p&gt;
&lt;h3 id="集合"&gt;集合&lt;/h3&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; chinese-random-name
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; nomnom
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; colorful
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; is-chinese
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;chinese-random-name&lt;/strong&gt;: &lt;a href="https://github.com/XadillaX/chinese-random-name" rel="nofollow" target="_blank"&gt;https://github.com/XadillaX/chinese-random-name&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nomnom&lt;/strong&gt;: &lt;a href="https://github.com/harthur/nomnom" rel="nofollow" target="_blank"&gt;https://github.com/harthur/nomnom&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;colorful&lt;/strong&gt;: &lt;a href="https://github.com/lepture/colorful" rel="nofollow" target="_blank"&gt;https://github.com/lepture/colorful&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;is-chinese&lt;/strong&gt;: &lt;a href="https://github.com/alsotang/is-chinese" rel="nofollow" target="_blank"&gt;https://github.com/alsotang/is-chinese&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="开工"&gt;开工&lt;/h2&gt;
&lt;p&gt;其结果在&lt;a href="https://github.com/BoogeeDoo/hua" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="解析命令行参数"&gt;解析命令行参数&lt;/h3&gt;
&lt;p&gt;首先效果是这样的：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hua &lt;span class="nt"&gt;--help&lt;/span&gt;

Usage: hua &lt;span class="o"&gt;[&lt;/span&gt;options]

Options:
&lt;span class="nt"&gt;-p&lt;/span&gt; PREFIX, &lt;span class="nt"&gt;--prefix&lt;/span&gt; PREFIX          to specify a prefix.
&lt;span class="nt"&gt;-s&lt;/span&gt; SUFFIX, &lt;span class="nt"&gt;--suffix&lt;/span&gt; SUFFIX          to specify a suffix.
&lt;span class="nt"&gt;-5&lt;/span&gt; WUXING, &lt;span class="nt"&gt;--five-elements&lt;/span&gt; WUXING   the file elements &lt;span class="o"&gt;(&lt;/span&gt;Wuxing&lt;span class="o"&gt;)&lt;/span&gt; of huaming.
&lt;span class="nt"&gt;-c&lt;/span&gt; COUNT, &lt;span class="nt"&gt;--count&lt;/span&gt; COUNT             the count of huaming  &lt;span class="o"&gt;[&lt;/span&gt;10]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用者可以自己想一个前缀或者后缀，然后自定义（或者也可以不指定）两个字的五行，以及指定一次性生成多少个花名。&lt;/p&gt;

&lt;p&gt;比如想要生成以 &lt;code&gt;龙&lt;/code&gt; 字为前缀的花名，就可以 &lt;code&gt;$ hua --prefix 龙&lt;/code&gt;，得到结果可以是这样的：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt; 龙幼
&lt;span class="k"&gt;*&lt;/span&gt; 龙巡
&lt;span class="k"&gt;*&lt;/span&gt; 龙躬
&lt;span class="k"&gt;*&lt;/span&gt; 龙仇
&lt;span class="k"&gt;*&lt;/span&gt; 龙锤
&lt;span class="k"&gt;*&lt;/span&gt; 龙镒
&lt;span class="k"&gt;*&lt;/span&gt; 龙拾
&lt;span class="k"&gt;*&lt;/span&gt; 龙央
&lt;span class="k"&gt;*&lt;/span&gt; 龙些
&lt;span class="k"&gt;*&lt;/span&gt; 龙悠
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果想两个字分别所属金和谁，就可以 &lt;code&gt;$ hua --five-elements 金水&lt;/code&gt; 来起花名：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt; 倩娥
&lt;span class="k"&gt;*&lt;/span&gt; 雀效
&lt;span class="k"&gt;*&lt;/span&gt; 黍棓
&lt;span class="k"&gt;*&lt;/span&gt; 姹溶
&lt;span class="k"&gt;*&lt;/span&gt; 馨沙
&lt;span class="k"&gt;*&lt;/span&gt; 宫闲
&lt;span class="k"&gt;*&lt;/span&gt; 裕混
&lt;span class="k"&gt;*&lt;/span&gt; 俗封
&lt;span class="k"&gt;*&lt;/span&gt; 绸娥
&lt;span class="k"&gt;*&lt;/span&gt; 瑞淦
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想要得到这样一个命令行参数，我们可以用 &lt;code&gt;nomnom&lt;/code&gt; 来解决。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nomnom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hua&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prefix&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;abbr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;to specify a prefix.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;metavar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PREFIX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suffix&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;abbr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;to specify a suffix.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;metavar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SUFFIX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;abbr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;the file elements (Wuxing) of huaming.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;metavar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WUXING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;abbr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;the count of huaming&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;metavar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COUNT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的这段代码分别指定了脚本名为 &lt;code&gt;hua&lt;/code&gt;，然后指定了 &lt;code&gt;prefix&lt;/code&gt; / &lt;code&gt;suffix&lt;/code&gt; / &lt;code&gt;five-elements&lt;/code&gt; 和 &lt;code&gt;count&lt;/code&gt; 四个参数，并把解析好的参数赋值给 &lt;code&gt;opts&lt;/code&gt; 变量。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;由于我希望这个包在通常的 Node.js 下都可以跑，所以没有用 &lt;code&gt;let&lt;/code&gt; 之类的东西。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="花名类"&gt;花名类&lt;/h3&gt;
&lt;p&gt;接下去要写一个花名类，这个类不只是可以在 CLI 之中使用，也可以让别人作为一个包来引入。然后实际上这个类就是要对 &lt;code&gt;chinese-random-name&lt;/code&gt; 进行一个封装。&lt;/p&gt;
&lt;h4 id="构造函数"&gt;构造函数&lt;/h4&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Hua&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Do something...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先这个 &lt;code&gt;options&lt;/code&gt; 就是之前由 &lt;code&gt;nomnom&lt;/code&gt; 解析出来的参数，当然有些参数是可选的。&lt;/p&gt;

&lt;p&gt;接下去我们要在构造函数也就是 &lt;code&gt;Hua&lt;/code&gt; 里面格式化前缀或者后缀（如果有的话），将他们弄成只有一个汉字的格式。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isChinese&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isChinese&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;前后缀弄好之后要对五行进行分析了。&lt;/p&gt;

&lt;p&gt;如果有前后缀那么忽略五行参数。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果有前缀，那么忽略传进来的五行的第一个五行；如果有后缀那么忽略第二个字的五行。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;wuxing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;金木水火土&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;wuxing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;wuxing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果前后缀都没有，那么要格式化一下该参数，使其仅剩两个有效的五行汉字。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 如果是无效五行或者冰没有这个字的话，随机一个五行出来&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;wuxing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wuxing&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 字数不够，随机来凑&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wuxing&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上的这些逻辑都写在构造函数里面，如果想要完整的构造函数可以看 &lt;code&gt;hua&lt;/code&gt; 的 &lt;a href="https://github.com/BoogeeDoo/hua/blob/09f1bc7eae14faaa9e881392d3811a6073d94b1c/lib/hua.js#L14-L55" rel="nofollow" target="_blank" title=""&gt;hua.js&lt;/a&gt; 文件。&lt;/p&gt;
&lt;h4 id="生成一个花名"&gt;生成一个花名&lt;/h4&gt;
&lt;p&gt;生成一个花名其实就是调用 &lt;code&gt;randomName.names.get&lt;/code&gt; 系列函数们了。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;有前后缀：&lt;/strong&gt;直接返回前缀加后缀。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;有前缀：&lt;/strong&gt;返回前缀加 &lt;code&gt;get1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;有后缀：&lt;/strong&gt;返回 &lt;code&gt;get1&lt;/code&gt; 加后缀。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;没有前后缀：&lt;/strong&gt;直接返回 &lt;code&gt;get2&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;以上情况都会传进（哪怕是 &lt;code&gt;undefined&lt;/code&gt;）五行参数。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以 &lt;code&gt;generateOne&lt;/code&gt; 函数长&lt;a href="https://github.com/BoogeeDoo/hua/blob/09f1bc7eae14faaa9e881392d3811a6073d94b1c/lib/hua.js#L67-L83" rel="nofollow" target="_blank" title=""&gt;这样&lt;/a&gt;：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Hua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateOne&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;randomName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;randomName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;randomName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five-elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="生成 Count 个花名"&gt;生成 Count 个花名&lt;/h4&gt;
&lt;p&gt;还记得 CLI 的 &lt;code&gt;count&lt;/code&gt; 参数么？因为为了方便，我们可以批量生成花名，所以就需要生成 Count 个花名的函数了。&lt;/p&gt;

&lt;p&gt;实际上就是一个循环调用 &lt;code&gt;generateOne&lt;/code&gt; 的函数而已。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Hua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateOne&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="CLI 文件"&gt;CLI 文件&lt;/h3&gt;
&lt;p&gt;刚刚那个 &lt;code&gt;nomnom&lt;/code&gt; 解析就在这个文件里面，然后接下去就是实例化一个 &lt;code&gt;Hua&lt;/code&gt; 对象，然后生成 &lt;code&gt;count&lt;/code&gt; 个花名。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hua&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hua&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后把花名输出来就好了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; * &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="桥豆麻袋！"&gt;桥豆麻袋！&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;说好的五行颜色呢？！&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好像是的哦，我们要在输出之前给 &lt;code&gt;result&lt;/code&gt; 上个色儿。&lt;/p&gt;

&lt;p&gt;遍历 &lt;code&gt;result&lt;/code&gt; 里面的花名每个字，获取它的五行属性，然后涮上色儿。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chinese-random-name&lt;/code&gt; 暴露了字典中每个字的五行属性，只需要赋值一下就好了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chinese-random-name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后逐一对比。最后对应金木水火土的颜色值分别为：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;definedColors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;197&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;220 为黄色，代表金；83 为绿色，代表木；26 蓝色代表水；197 红色代表火；59 灰色代表土。&lt;/p&gt;

&lt;p&gt;如果那个字无法找到属性，则不上色，保持默认。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;withColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;wuxing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;wx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wuxing&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wx&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;withColor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;wx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fgcolor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;definedColors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="nx"&gt;withColor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;withColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此我们的 CLI 就写好了，最后别忘了在 &lt;a href="https://github.com/BoogeeDoo/hua/blob/09f1bc7eae14faaa9e881392d3811a6073d94b1c/bin/hua" rel="nofollow" target="_blank" title=""&gt;hua&lt;/a&gt; 这个 CLI 文件顶部加上一句话。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env node&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这代表到时候如果要 &lt;code&gt;./hua&lt;/code&gt; 的时候这个脚本是用 Node.js 来跑的。&lt;/p&gt;
&lt;h2 id="收拾"&gt;收拾&lt;/h2&gt;
&lt;p&gt;本来想好好写篇起花名的牢骚，结果不知不觉写成了给初心者看的初级教程了，泪目 ( •̥́ ˍ •̀ू )&lt;/p&gt;

&lt;p&gt;不嫌弃的就这么看看吧。&lt;/p&gt;

&lt;p&gt;最后这里给出我写好的这个 &lt;code&gt;hua&lt;/code&gt; 程序。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; huaming
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就能在命令行下面跑了，跑法上面几章有介绍过。它的 Repo 在&lt;a href="https://github.com/BoogeeDoo/hua" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;最后希望这个包在你们起花名的时候还真有那么一丢丢的用处。&lt;/p&gt;</description>
      <author>xadillax</author>
      <pubDate>Wed, 24 Feb 2016 16:03:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/29082</link>
      <guid>https://ruby-china.org/topics/29082</guid>
    </item>
    <item>
      <title>Node.js 启动方式：一道关于全局变量的题目引发的思考·续</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;原文地址：&lt;a href="https://xcoder.in/2015/11/27/a-js-problem-about-global-continued/" rel="nofollow" target="_blank"&gt;https://xcoder.in/2015/11/27/a-js-problem-about-global-continued/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本文是上文《&lt;a href="https://ruby-china.org/topics/28207" title=""&gt;Node.js 启动方式：一道关于全局变量的题目引发的思考&lt;/a&gt;》的续章。&lt;/p&gt;
&lt;h2 id="原题回顾"&gt;原题回顾&lt;/h2&gt;
&lt;p&gt;我们还是先回顾下原题吧。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;  
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;上题由我们亲爱的&lt;a href="http://f2e.souche.com/blog/author/wang-xing-long/" rel="nofollow" target="_blank" title=""&gt;小龙&lt;/a&gt;童鞋发现并在我们的 901 群里提问的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;不过在上面一篇文章中，我们讲的是在 REPL 和 &lt;code&gt;vm&lt;/code&gt; 中有什么事情，但是并没有解释为什么在文件模块的载入形式下，&lt;code&gt;var&lt;/code&gt; 并不会挂载到全局变量去。&lt;/p&gt;

&lt;p&gt;其实原因很简单，大家应该也都明白，在 Node.js 中，每个文件相当于是一个闭包，在 &lt;code&gt;require&lt;/code&gt; 的时候被编译包了起来。&lt;/p&gt;

&lt;p&gt;但是具体是怎么样的呢？虽然网上也有很多答案，我还是决定在这里按上一篇文章的尿性稍微解释一下。&lt;/p&gt;
&lt;h2 id="分析"&gt;分析&lt;/h2&gt;
&lt;p&gt;首先我们还是回到上一篇文章的《Node REPL 启动的沙箱》一节，里面说了当启动 Node.js 的时候是以 &lt;a href="https://github.com/nodejs/node/blob/dfee4e3712ac4673b5fc472a8f77ac65bdc65f87/src/node.js" rel="nofollow" target="_blank" title=""&gt;src/node.js&lt;/a&gt; 为入口的。&lt;/p&gt;

&lt;p&gt;如果以 REPL 为途径启动的话是直接启动一个 &lt;code&gt;vm&lt;/code&gt;，而此时的所有根级变量都在最顶级的作用域下，所以一个 &lt;code&gt;var&lt;/code&gt; 自然会绑定到 &lt;code&gt;global&lt;/code&gt; 下面了。&lt;/p&gt;

&lt;p&gt;而如果是以文件，即 &lt;code&gt;$ node foo.js&lt;/code&gt; 形式启动的话，它就会执行 src/node.js 里面的另一坨条件分支了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// make process.argv[1] into a full path&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preloadModules&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v8debug&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execArgv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^--debug-brk&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;=&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)?&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;debugTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_DEBUG_TIMEOUT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runMain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debugTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Main entry point into most programs:&lt;/span&gt;
    &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runMain&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从上面的代码看出，只要是以 &lt;code&gt;$ node foo.js&lt;/code&gt; 形式启动的，都会经历 &lt;code&gt;startup.preloadModules()&lt;/code&gt; 和 &lt;code&gt;Module.runMain()&lt;/code&gt; 两个函数。&lt;/p&gt;
&lt;h3 id="startup.preloadModules()"&gt;startup.preloadModules()&lt;/h3&gt;
&lt;p&gt;我们来看看&lt;a href="https://github.com/nodejs/node/blob/dfee4e3712ac4673b5fc472a8f77ac65bdc65f87/src/node.js#L870" rel="nofollow" target="_blank" title=""&gt;这个函数&lt;/a&gt;。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preloadModules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_preload_modules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;_preloadModules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_preload_modules&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际上就是执行的 lib/module.js 里面的 &lt;code&gt;_preloadModules&lt;/code&gt; 函数，并且把这个 &lt;code&gt;process._preload_modules&lt;/code&gt; 给传进去。当然，前提是有这个 &lt;code&gt;process._preload_modules&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id="process._preload_modules"&gt;process._preload_modules&lt;/h4&gt;
&lt;p&gt;这个 &lt;code&gt;process._preload_modules&lt;/code&gt; 指的就是当你在使用 Node.js 的时候，命令行里面的 &lt;code&gt;--require&lt;/code&gt; 参数。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-r, --require         module to preload (option can be repeated)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码在 &lt;a href="https://github.com/nodejs/node/blob/master/src/node.cc#L3306" rel="nofollow" target="_blank" title=""&gt;src/node.cc&lt;/a&gt; 里面可考。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strcmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"--require"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
           &lt;span class="n"&gt;strcmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%s: %s requires an argument&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;args_consumed&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;local_preload_modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;preload_module_count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果遇到了 &lt;code&gt;--require&lt;/code&gt; 这个参数，则对静态变量 &lt;code&gt;local_preload_modules&lt;/code&gt; 和 &lt;code&gt;preload_module_count&lt;/code&gt; 做处理，把这个预加载模块路径加进去。&lt;/p&gt;

&lt;p&gt;待到&lt;a href="https://github.com/nodejs/node/blob/master/src/node.cc#L2933" rel="nofollow" target="_blank" title=""&gt;要生成 &lt;code&gt;process&lt;/code&gt; 这个变量的时候&lt;/a&gt;，再把预加载模块的信息放到 &lt;code&gt;process._preload_modules&lt;/code&gt; 里面去。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetupProcessObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;exec_argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;exec_argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preload_module_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;CHECK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preload_modules&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;preload_module_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NewFromUtf8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                                 &lt;span class="n"&gt;preload_modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;READONLY_PROPERTY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="s"&gt;"_preload_modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;preload_modules&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;preload_modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;preload_module_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最重要的就是这句&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;READONLY_PROPERTY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s"&gt;"_preload_modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="require('module')._preloadModules"&gt;require('module')._preloadModules&lt;/h4&gt;
&lt;p&gt;上面我们讲了这个 &lt;code&gt;process._preload_modules&lt;/code&gt;，然后现在我们说说是如何把 &lt;code&gt;$ node --require bar.js foo.js&lt;/code&gt; 给预加载进去的。&lt;/p&gt;

&lt;p&gt;接下去我们就要移步到 lib/module.js 文件里面去了。&lt;/p&gt;

&lt;p&gt;在&lt;a href="https://github.com/nodejs/node/blob/e25f8683f1735f55a27c00d41691be286f50e13f/lib/module.js#L496" rel="nofollow" target="_blank" title=""&gt;第 496 行左右&lt;/a&gt;的地方有这个函数。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_preloadModules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Preloaded modules have a dummy parent module which is deemed to exist&lt;/span&gt;
  &lt;span class="c1"&gt;// in the current working directory. This seeds the search path for&lt;/span&gt;
  &lt;span class="c1"&gt;// preloaded modules.&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;internal/preload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_nodeModulePaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ENOENT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;大概我们能看到，就是以 &lt;code&gt;internal/preload&lt;/code&gt; 为 ID 的 Module 对象来载入这些预加载模块。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;internal/preload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据这个函数的注释说明，这个 Module 对象是一个虚拟的 Module 对象，主要是跟非预加载的那些模块给隔离或者区别开来，并且提供一个模块搜索路径。&lt;/p&gt;
&lt;h3 id="Module.runMain()"&gt;Module.runMain()&lt;/h3&gt;
&lt;p&gt;看完上面的说明，我们接下去看看 &lt;code&gt;Module.runMain()&lt;/code&gt; 函数。&lt;/p&gt;

&lt;p&gt;这个函数还是位于 &lt;a href="https://github.com/nodejs/node/blob/e25f8683f1735f55a27c00d41691be286f50e13f/lib/module.js#L455" rel="nofollow" target="_blank" title=""&gt;lib/module.js&lt;/a&gt;  文件里面。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runMain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Load the main module--the command line argument.&lt;/span&gt;
  &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Handle any nextTicks added in the first tick of the program&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_tickCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们看到了就是在这句话中，Module 载入了 &lt;code&gt;process.argv[1]&lt;/code&gt; 也就是文件名，自此一发不可收拾。&lt;/p&gt;
&lt;h4 id="Module._load"&gt;Module._load&lt;/h4&gt;
&lt;p&gt;这个函数相信很多人都知道它的用处了，无非就是载入文件，并加载到一个闭包里面。&lt;/p&gt;

&lt;p&gt;这样一来在文件里面 &lt;code&gt;var&lt;/code&gt; 出来的变量就不在根作用域下面了，所以不会粘到 &lt;code&gt;global&lt;/code&gt; 里面去。它的 &lt;code&gt;this&lt;/code&gt; 就是包起来的这个闭包了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isMain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_resolveFilename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cachedModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cachedModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nonInternalExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load native module %s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mainModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面的代码首先是根据传入的文件名找到真的文件地址，就是所谓的搜索路径了。比如 &lt;code&gt;require("foo")&lt;/code&gt; 就会分别从 &lt;code&gt;node_modules&lt;/code&gt; 路径等依次查找下来。&lt;/p&gt;

&lt;p&gt;我经常 Hack 这个 &lt;code&gt;_resolveFilename&lt;/code&gt; 函数来简化 &lt;code&gt;require&lt;/code&gt; 函数，比如我希望我用 &lt;code&gt;require("controller/foo")&lt;/code&gt; 就能直接拿到 ./src/controller/foo.js 文件。有兴趣讨论一下这个用法的童鞋可以转到我的 &lt;a href="https://gist.github.com/XadillaX/bc0e7c92925de0647477" rel="nofollow" target="_blank" title=""&gt;Gist&lt;/a&gt; 上查看 Hack 的一个 Demo。&lt;/p&gt;

&lt;p&gt;第二步就是我们常说的缓存了。如果这个模块之前加载过，那么在 &lt;code&gt;Module._cache&lt;/code&gt; 下面会有个缓存，直接去取就是了。&lt;/p&gt;

&lt;p&gt;第三步就是看看是不是 &lt;code&gt;NativeModule&lt;/code&gt;。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nonInternalExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load native module %s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="NativeModule"&gt;NativeModule&lt;/h5&gt;
&lt;p&gt;之前的代码里面其实也没少出现这个 &lt;code&gt;NativeModule&lt;/code&gt;。那这个 &lt;code&gt;NativeModule&lt;/code&gt; 到底是个 shenmegui 呢？&lt;/p&gt;

&lt;p&gt;其实它还是在 Node.js 的入口 &lt;a href="https://github.com/nodejs/node/blob/e25f8683f1735f55a27c00d41691be286f50e13f/src/node.js#L886" rel="nofollow" target="_blank" title=""&gt;src/node.js&lt;/a&gt; 里面。&lt;/p&gt;

&lt;p&gt;它主要用来加载 Node.js 的一些原生模块，比如说 &lt;code&gt;NativeModule.require("child_process")&lt;/code&gt; 等，也用于一些 &lt;code&gt;internal&lt;/code&gt; 模块的载入，比如 &lt;code&gt;NativeModule.require("internal/repl")&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;之前代码的这个判断就是说如果判断要载入的文件是一个原生模块，那么就使用 &lt;code&gt;NativeModule.require&lt;/code&gt; 来载入。&lt;/p&gt;
&lt;h6 id="NativeModule.require"&gt;NativeModule.require&lt;/h6&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;native_module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No such native module &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleLoadList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NativeModule &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;nativeModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;nativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;nativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先看看是否是本身，再看看是否被缓存，然后看看是否合法。接下去就是填充 &lt;code&gt;process.moduleLoadList&lt;/code&gt;，最后载入这个原生模块、缓存、编译并返回。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;有兴趣的同学可以在 Node.js 中输出 &lt;code&gt;process.moduleLoadList&lt;/code&gt; 看看。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这个 &lt;code&gt;compile&lt;/code&gt; 很重要。&lt;/p&gt;
&lt;h6 id="NativeModule.prototype.compile"&gt;NativeModule.prototype.compile&lt;/h6&gt;
&lt;p&gt;在 &lt;code&gt;NativeModule&lt;/code&gt; 编译的过程中，大概的步骤是获取代码、包裹（Wrap）代码，把包裹的代码 &lt;code&gt;runInContext&lt;/code&gt; 一遍得到包裹好的函数，然后执行一遍就算载入好了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;runInThisContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们往这个 src/node.js 文件这个函数的上面几行看一下，就知道包裹代码是怎么回事了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(function (exports, require, module, __filename, __dirname) {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;});&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据上面的代码，我们能知道的就是比如我们一个内置模块的代码是：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么包裹好的代码将会是这样子的：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样一看就明白了这些 &lt;code&gt;require&lt;/code&gt;、&lt;code&gt;module&lt;/code&gt;、&lt;code&gt;exports&lt;/code&gt;、&lt;code&gt;__filename&lt;/code&gt; 和 &lt;code&gt;__dirname&lt;/code&gt; 是怎么来了吧。&lt;/p&gt;

&lt;p&gt;当我们通过 &lt;code&gt;var fn = runInThisContext(source, { filename: this.filename });&lt;/code&gt; 得到了这个包裹好的函数之后，我们就把相应的参数传进这个闭包函数去执行。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个 &lt;code&gt;this&lt;/code&gt; 就是对应的这个 &lt;code&gt;module&lt;/code&gt;，自然这个 &lt;code&gt;module&lt;/code&gt; 里面就有它的 &lt;code&gt;exports&lt;/code&gt;；&lt;code&gt;require&lt;/code&gt; 函数就是 &lt;code&gt;NativeModule.require&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以我们看到的在 &lt;code&gt;lib/*.js&lt;/code&gt; 文件里面的那些 &lt;code&gt;require&lt;/code&gt; 函数，实际上就是包裹好之后的代码的 &lt;code&gt;NativeModule.require&lt;/code&gt; 了。&lt;/p&gt;

&lt;p&gt;所以说实际上这些内置模块内部的根作用域下的 &lt;code&gt;var&lt;/code&gt; 再怎么样高级也都是在包裹好的闭包里面 &lt;code&gt;var&lt;/code&gt;，怎么的也跟 &lt;code&gt;global&lt;/code&gt; 搭不着边。&lt;/p&gt;
&lt;h6 id="内部原生模块"&gt;内部原生模块&lt;/h6&gt;
&lt;p&gt;通过上面的追溯我们知道了，如果我们在代码里面使用 &lt;code&gt;require&lt;/code&gt; 的话，会先看看这个模块是不是原生模块。&lt;/p&gt;

&lt;p&gt;不过回过头看一下它的这个判断条件：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nonInternalExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果是原生模块并且不是原生内部模块的话。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;那是怎么区分原生模块和内部原生模块呢？&lt;/p&gt;

&lt;p&gt;我们再来看看这个 &lt;code&gt;NativeModule.nonInternalExists(filename)&lt;/code&gt; 函数。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nonInternalExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isInternal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isInternal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;internal/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;上面的代码是去除各种杂七杂八的条件之后的一种情况，别的情况还请各位童鞋自行看 Node.js 源码。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;也就是说我们在我们自己的代码里面是请求不到 Node.js 源码里面 &lt;code&gt;lib/internal/*.js&lt;/code&gt; 这些文件的——因为它们被上面的这个条件分支给过滤了。（比如 &lt;code&gt;require("internal/module")&lt;/code&gt; 在自己的代码里面是无法运行的）&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt; 不过有一个例外，那就是 &lt;code&gt;require("internal/repl")&lt;/code&gt;。详情可以参考这个 &lt;a href="https://github.com/nodejs/node/issues/3393" rel="nofollow" target="_blank" title=""&gt;Issue&lt;/a&gt; 和&lt;a href="https://github.com/nodejs/node/blob/9148114c93861359a502801499d4c26d0b761174/lib/module.js#L276-L277" rel="nofollow" target="_blank" title=""&gt;这段代码&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id="Module.prototype.load"&gt;Module.prototype.load&lt;/h5&gt;
&lt;p&gt;解释完了上面的 &lt;code&gt;NativeModule&lt;/code&gt; 之后，我们要就上面 &lt;code&gt;Module._load&lt;/code&gt; 里面的下一步 &lt;code&gt;module.load&lt;/code&gt; 也就是 &lt;code&gt;Module.prototype.load&lt;/code&gt; 做解析了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_extensions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_extensions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;做了一系列操作之后得到了真·文件名，然后判断一下后缀。如果是 &lt;code&gt;".js"&lt;/code&gt; 的话执行 &lt;code&gt;Module._extensions[".js"]&lt;/code&gt; 这个函数去编译代码，如果是 &lt;code&gt;".json"&lt;/code&gt; 则是 &lt;code&gt;Module._extensions[".json"]&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这里我们略过 JSON 和 C++ Addon，直奔 &lt;code&gt;Module._extensions[".js"]&lt;/code&gt;。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_extensions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;internalModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stripBOM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它也很简单，就是奔着 &lt;code&gt;_compile&lt;/code&gt; 去的。&lt;/p&gt;
&lt;h6 id="Module.prototype._compile"&gt;Module.prototype._compile&lt;/h6&gt;
&lt;p&gt;先上&lt;a href="https://github.com/nodejs/node/blob/e25f8683f1735f55a27c00d41691be286f50e13f/lib/module.js#L378-L426" rel="nofollow" target="_blank" title=""&gt;代码&lt;/a&gt;。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_compile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// remove shebang&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shebangRe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_resolveFilename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mainModule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Enable support to add extra extension types&lt;/span&gt;
  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// create wrapper function&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;compiledWrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;runInThisContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineOffset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compiledWrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;感觉流程上跟 &lt;code&gt;NativeModule&lt;/code&gt; 的编译相似，不过这里是事先准备好要在载入的文件里面用的 &lt;code&gt;require&lt;/code&gt; 函数，以及一些 &lt;code&gt;require&lt;/code&gt; 的周边。&lt;/p&gt;

&lt;p&gt;接下去就是用 &lt;code&gt;Module.wrap&lt;/code&gt; 来包裹代码了，包裹完之后把得到的函数用参数 &lt;code&gt;self.exports, require, self, filename, dirname&lt;/code&gt; 去执行一遍，就算是文件载入完毕了。&lt;/p&gt;

&lt;p&gt;最后回到之前载入代码的那一刻，把载入完毕得到的 &lt;code&gt;module.exports&lt;/code&gt; 再 &lt;code&gt;return&lt;/code&gt; 出去就好了。&lt;/p&gt;
&lt;h6 id="Module.wrap"&gt;Module.wrap&lt;/h6&gt;
&lt;p&gt;这个就不用说了。&lt;/p&gt;

&lt;p&gt;在 lib/module.js 的&lt;a href="https://github.com/nodejs/node/blob/e25f8683f1735f55a27c00d41691be286f50e13f/lib/module.js#L46-L48" rel="nofollow" target="_blank" title=""&gt;最顶端附近&lt;/a&gt;有这么几行代码。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debuglog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一切豁然开朗了吧。&lt;/p&gt;

&lt;p&gt;连 &lt;code&gt;NativeModule&lt;/code&gt; 的代码都逃不开被之前说的闭包所包裹，那么你自己写的 JS 文件当然也会被 &lt;code&gt;NativeModule.wrap&lt;/code&gt; 所包裹。&lt;/p&gt;

&lt;p&gt;那么你在代码根作用域申明的函数实际上在运行时里面已经被一个闭包给包住了。&lt;/p&gt;

&lt;p&gt;以前可能很多同学只知道是被闭包包住了，但是包的方法、流程今天算是解析了一遍了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;  
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个 &lt;code&gt;var a&lt;/code&gt; 怎么也不可能绑到 &lt;code&gt;global&lt;/code&gt; 去啊。&lt;/p&gt;
&lt;h6 id="Module.prototype.require"&gt;Module.prototype.require&lt;/h6&gt;
&lt;p&gt;虽然我们上面讲得差不多了，可能很多童鞋也厌烦了。&lt;/p&gt;

&lt;p&gt;不过该讲完的还是得讲完。&lt;/p&gt;

&lt;p&gt;我们在我们自己文件中用的 &lt;code&gt;require&lt;/code&gt; 在上一节里面有提到过，传到我们闭包里面的 &lt;code&gt;require&lt;/code&gt; 实际上是长这样的：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以实际上就是个 &lt;code&gt;Module.prototype.require&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;我们再看看&lt;a href="https://github.com/nodejs/node/blob/e25f8683f1735f55a27c00d41691be286f50e13f/lib/module.js#L362-L366" rel="nofollow" target="_blank" title=""&gt;这个函数&lt;/a&gt;。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;missing path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path must be a string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一下子又绕回到了我们一开始的 &lt;code&gt;Module._load&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;所以基本上就差不多到这过了。&lt;/p&gt;
&lt;h2 id="REPL vs 文件启动"&gt;REPL vs 文件启动&lt;/h2&gt;
&lt;p&gt;最后我们再点一下，或者说回顾一下吧。&lt;/p&gt;

&lt;p&gt;REPL 启动的时候 Node.js 是开了个 &lt;code&gt;vm&lt;/code&gt; 直接让你跑，并没有把代码包在一个闭包里面，所以再根作用域下的变量会 &lt;code&gt;Biu&lt;/code&gt; 一下贴到 &lt;code&gt;global&lt;/code&gt; 中去。&lt;/p&gt;

&lt;p&gt;而文件启动的时候，会做本文中说的一系列事情，然后就会把各文件都包到一个闭包去，所以变量就无法通过这种方式来贴到 &lt;code&gt;global&lt;/code&gt; 去了。&lt;/p&gt;

&lt;p&gt;不过这种二义性会在 &lt;code&gt;"use strict";&lt;/code&gt; 中戛然而止。&lt;/p&gt;

&lt;p&gt;珍爱生命，&lt;code&gt;use strict&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="小结"&gt;小结&lt;/h2&gt;
&lt;p&gt;本文可能很多童鞋看完后悔觉得很坑——JS 为什么有那么多二义性那么坑呢。&lt;/p&gt;

&lt;p&gt;其实不然，主要是可能很多人对 Node.js 执行的机制不是很了解。&lt;/p&gt;

&lt;p&gt;本文从小龙抛出的一个简单问题进入，然后浅入浅出 Node.js 的一些执行机制什么的，希望对大家还是有点帮助，更何况我在意的不是问题本身，而是分析的这个过程。&lt;/p&gt;
&lt;h2 id="番外"&gt;番外&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;以下均为臆想。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;小龙：&lt;/strong&gt; 喂喂喂，我就问一个简单的小破题目，你至于嘛！&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>xadillax</author>
      <pubDate>Fri, 27 Nov 2015 15:13:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/28217</link>
      <guid>https://ruby-china.org/topics/28217</guid>
    </item>
    <item>
      <title>Node.js 启动方式：一道关于全局变量的题目引发的思考</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;原文链接：&lt;a href="https://xcoder.in/2015/11/26/a-js-problem-about-global/" rel="nofollow" target="_blank"&gt;https://xcoder.in/2015/11/26/a-js-problem-about-global/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本人是作者。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="原题"&gt;原题&lt;/h2&gt;
&lt;p&gt;  题目是这样的。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;上题由我们亲爱的&lt;a href="http://f2e.souche.com/blog/author/wang-xing-long/" rel="nofollow" target="_blank" title=""&gt;小龙&lt;/a&gt;童鞋发现并在我们的 901 群里提问的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="经过"&gt;经过&lt;/h2&gt;
&lt;p&gt;  然后有下面的小对话。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;小龙：&lt;/strong&gt;你们猜这个输出什么？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;弍纾：&lt;/strong&gt;2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;力叔：&lt;/strong&gt;2 啊&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;死月·絲卡蕾特：&lt;/strong&gt;2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;力叔：&lt;/strong&gt;有什么问题么？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;小龙：&lt;/strong&gt;输出 undefind。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;死月·絲卡蕾特：&lt;/strong&gt;你确定？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;小龙：&lt;/strong&gt;是不是我电脑坏了&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;力叔：&lt;/strong&gt;你确定？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;弍纾：&lt;/strong&gt;你确定？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;小龙：&lt;/strong&gt;为什么我 node 文件名跑出来的是 undefined？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;郑昱：&lt;/strong&gt;-.- 一样阿。undefined&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  以上就是刚见到这个题目的时候群里的一个小讨论。&lt;/p&gt;
&lt;h2 id="分析"&gt;分析&lt;/h2&gt;
&lt;p&gt;  后来我就觉得奇怪，既然小龙验证过了，说明他也不是随地大小便，无的放矢什么的。&lt;/p&gt;

&lt;p&gt;  于是我也验证了一下，不过由于偷懒，没有跟他们一样写在文件里面，而是直接 node 开了个 REPL 来输入上述代码。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结果是 2！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结果是 2！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结果是 2！&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  于是这就出现了一个很奇怪的问题。&lt;/p&gt;

&lt;p&gt;  尼玛为毛我是 &lt;code&gt;2&lt;/code&gt; 他们俩是 &lt;code&gt;undefined&lt;/code&gt; 啊！&lt;/p&gt;

&lt;p&gt;  不过马上我就反应过来了——我们几个的环境不同，他们是 &lt;code&gt;$ node foo.js&lt;/code&gt; 而我是直接 node 开了个 REPL，所以有一定的区别。&lt;/p&gt;

&lt;p&gt;  而力叔本身就是前端大神，我估计是以 Chrome 的调试工具下为基础出的答案。&lt;/p&gt;
&lt;h2 id="REPL vs 文件执行"&gt;REPL vs 文件执行&lt;/h2&gt;
&lt;p&gt;  其实上述的问题，需要解释的问题大概就是 &lt;code&gt;a&lt;/code&gt; 到底挂在哪了。&lt;/p&gt;

&lt;p&gt;  因为细细一想，在 &lt;code&gt;function&lt;/code&gt; 当中，&lt;code&gt;this&lt;/code&gt; 指向的目标是 &lt;code&gt;global&lt;/code&gt; 或者 &lt;code&gt;window&lt;/code&gt;。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;还无法理解上面这句话的童鞋需要先补一下基础。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  那么最终需要解释的就是 &lt;code&gt;a&lt;/code&gt; 到底有没有挂在全局变量上面。&lt;/p&gt;

&lt;p&gt;  这么一想就有点细思恐极的味道了——如果在 node 线上运行环境里面的源代码文件里面随便 &lt;code&gt;var&lt;/code&gt; 一个变量就挂到了全局变量里面那是有多恐怖！&lt;/p&gt;

&lt;p&gt;  于是就有些释然了。&lt;/p&gt;

&lt;p&gt;  但究竟是什么原因导致 REPL 和文件执行方式不一样的呢？&lt;/p&gt;
&lt;h3 id="全局对象的属性"&gt;全局对象的属性&lt;/h3&gt;
&lt;p&gt;  首先是弍纾找出了阮老师 ES6 系列文章中的&lt;a href="http://es6.ruanyifeng.com/#docs/let" rel="nofollow" target="_blank" title=""&gt;全局对象属性&lt;/a&gt;一节。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;全局对象是最顶层的对象，在浏览器环境指的是 window 象，在 Node.js 指的是 global 对象。ES5 之中，全局对象的属性与全局变量是等价的。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;

&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面代码中，全局对象的属性赋值与全局变量的赋值，是同一件事。（对于 Node 来说，这一条只对 REPL 环境适用，模块环境之中，全局变量必须显式声明成 global 对象的属性。）&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;有了阮老师的文章验证了这个猜想，我可以放心大胆继续看下去了。&lt;/p&gt;
&lt;h3 id="repl.js"&gt;repl.js&lt;/h3&gt;
&lt;p&gt;  知道了上文的内容之后，感觉首要查看的就是 Node.js 源码中的 &lt;a href="https://github.com/nodejs/node/blob/master/lib/repl.js#L513" rel="nofollow" target="_blank" title=""&gt;repl.js&lt;/a&gt; 了。&lt;/p&gt;

&lt;p&gt;  先是结合了一下自己以前用自定义 REPL 的情况，一般的步骤先是获取 REPL 的上下文，然后在上下文里面贴上各种自己需要的东西。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;relp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; ➜ &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 在 c 里面贴上各种上下文&lt;/span&gt;
&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;关于自定义 REPL 的一些使用方式可以参考下老雷写的《&lt;a href="https://cnodejs.org/topic/563735ed677332084c319d95" rel="nofollow" target="_blank" title=""&gt;Node.js 定制 REPL 的妙用&lt;/a&gt;》。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  有了之前写 REPL 的经验，大致明白了 REPL 里面有个上下文的东西，那么在 repl.js 里面我们也找到了类似的代码。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;REPLServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useGlobal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// make built-in modules available directly&lt;/span&gt;
  &lt;span class="c1"&gt;// (loaded lazily)&lt;/span&gt;
  &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_builtinLibs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// allow the creation of other globals with this name&lt;/span&gt;
      &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;configurable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  看到了关键字 &lt;code&gt;vm&lt;/code&gt;。我们暂时先不管 &lt;code&gt;vm&lt;/code&gt;，光从上面的代码可以看出，&lt;code&gt;context&lt;/code&gt; 要么等于 &lt;code&gt;global&lt;/code&gt;，要么就是把 &lt;code&gt;global&lt;/code&gt; 上面的所有东西都粘过来。&lt;/p&gt;

&lt;p&gt;  然后顺带着把必须的两个不在 &lt;code&gt;global&lt;/code&gt; 里的两个东西 &lt;code&gt;require&lt;/code&gt; 和 &lt;code&gt;module&lt;/code&gt; 给弄过来。&lt;/p&gt;

&lt;p&gt;  下面的东西就不需要那么关心了。&lt;/p&gt;
&lt;h3 id="VM"&gt;VM&lt;/h3&gt;
&lt;p&gt;  接下去我们来讲讲 &lt;code&gt;vm&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;  VM 是 node 中的一个内置模块，可以在&lt;a href="https://nodejs.org/dist/v4.2.2/docs/api/vm.html" rel="nofollow" target="_blank" title=""&gt;文档&lt;/a&gt;中看到说明和使用方法。&lt;/p&gt;

&lt;p&gt;  大致就是将代码运行在一个沙箱之内，并且事先赋予其一些 &lt;code&gt;global&lt;/code&gt; 变量。&lt;/p&gt;

&lt;p&gt;  而真正起到上述 &lt;code&gt;var&lt;/code&gt; 和 &lt;code&gt;global&lt;/code&gt; 区别的就是这个 &lt;code&gt;vm&lt;/code&gt; 了。&lt;/p&gt;

&lt;p&gt;  &lt;code&gt;vm&lt;/code&gt; 之中在根作用域（也就是最外层作用域）中使用 &lt;code&gt;var&lt;/code&gt; 应该是跟在浏览器中一样，会把变量粘到 &lt;code&gt;global&lt;/code&gt;（浏览器中是 &lt;code&gt;window&lt;/code&gt;）中去。&lt;/p&gt;

&lt;p&gt;  我们可以试试这样的代码：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;localVar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;initial value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runInThisContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var localVar = "vm";&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localVar: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;localVar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;global.localVar: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localVar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  其输出结果是：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;localVar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initial&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localVar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  如文档中所说，&lt;code&gt;vm&lt;/code&gt; 的一系列函数中跑脚本都无法对当前的局部变量进行访问。各函数能访问自己的 &lt;code&gt;global&lt;/code&gt;，而 &lt;code&gt;runInThisContext&lt;/code&gt; 的 &lt;code&gt;global&lt;/code&gt; 与当前上下文的 &lt;code&gt;global&lt;/code&gt; 是一样的，所以能访问当前的全局变量。&lt;/p&gt;

&lt;p&gt;  所以出现上述结果也是理所当然的了。&lt;/p&gt;

&lt;p&gt;  所以在 &lt;code&gt;vm&lt;/code&gt; 中跑我们一开始抛出的问题，答案自然就是 &lt;code&gt;2&lt;/code&gt; 了。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;console&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runInContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var a = 2;function foo(){console.log(this.a);}foo();&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Node REPL 启动的沙箱"&gt;Node REPL 启动的沙箱&lt;/h3&gt;
&lt;p&gt;  最后我们再只需要验证一件事就能真相大白了。&lt;/p&gt;

&lt;p&gt;  平时我们自定义一个 &lt;code&gt;repl.js&lt;/code&gt; 然后执行 &lt;code&gt;$ node repl.js&lt;/code&gt; 的话是会启动一个 REPL，而这个 REPL 会去调 &lt;code&gt;vm&lt;/code&gt;，所以会出现 &lt;code&gt;2&lt;/code&gt; 的答案；或者我们自己在代码里面写一个 &lt;code&gt;vm&lt;/code&gt; 然后跑之前的代码，也是理所当然出现 &lt;code&gt;2&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;  那么我们就输入 &lt;code&gt;$ node&lt;/code&gt; 来进入的 REPL 跟我们之前讲的 REPL 是不是同一个东西呢？&lt;/p&gt;

&lt;p&gt;  如果是的话，一切就释然了。&lt;/p&gt;

&lt;p&gt;  首先我们进入到 Node 的入口文件——C++ 的 &lt;code&gt;int main()&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;  它在 Node.js 源码 &lt;a href="https://github.com/nodejs/node/blob/0966ab99966b7d3fbe4d7b93797fb299595fca72/src/node_main.cc#L45" rel="nofollow" target="_blank" title=""&gt;src/node_main.cc&lt;/a&gt; 之中。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;setvbuf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_IOLBF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  就在主函数中执行了 &lt;code&gt;node::Start&lt;/code&gt;。而这个 &lt;code&gt;node::Start&lt;/code&gt; 又存在 &lt;a href="https://github.com/nodejs/node/blob/0966ab99966b7d3fbe4d7b93797fb299595fca72/src/node.cc#L4109" rel="nofollow" target="_blank" title=""&gt;src/node.cc&lt;/a&gt; 里面。&lt;/p&gt;

&lt;p&gt;  然后在 &lt;code&gt;node::Start&lt;/code&gt; 里面又调用 &lt;code&gt;StartNodeInstance&lt;/code&gt;，在这里面是 &lt;code&gt;LoadEnvironment&lt;/code&gt; 函数。&lt;/p&gt;

&lt;p&gt;  最后在 &lt;code&gt;LoadEnvironment&lt;/code&gt; 中看到了几句关键的语句：&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;script_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FIXED_ONE_BYTE_STRING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"node.js"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExecuteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MainSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;script_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//...&lt;/span&gt;

&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;::&lt;/span&gt;&lt;span class="n"&gt;Cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;process_object&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  还有这么一段关键的注释。&lt;/p&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Now we call 'f' with the 'process' variable that we've built up with&lt;/span&gt;
&lt;span class="c1"&gt;// all our bindings. Inside node.js we'll take care of assigning things to&lt;/span&gt;
&lt;span class="c1"&gt;// their places.&lt;/span&gt;

&lt;span class="c1"&gt;// We start the process this way in order to be more modular. Developers&lt;/span&gt;
&lt;span class="c1"&gt;// who do not like how 'src/node.js' setups the module system but do like&lt;/span&gt;
&lt;span class="c1"&gt;// Node's I/O bindings may want to replace 'f' with their own function.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  也就是说，启动 &lt;code&gt;node&lt;/code&gt; 的时候，在做了一些准备之后是开始载入执行 src 文件夹下面的 &lt;a href="https://github.com/nodejs/node/blob/0966ab99966b7d3fbe4d7b93797fb299595fca72/src/node.js" rel="nofollow" target="_blank" title=""&gt;node.js&lt;/a&gt; 文件。&lt;/p&gt;

&lt;p&gt;  在 &lt;a href="https://github.com/nodejs/node/blob/0966ab99966b7d3fbe4d7b93797fb299595fca72/src/node.js#L92" rel="nofollow" target="_blank" title=""&gt;92 行&lt;/a&gt;附近有针对 &lt;code&gt;$ node foo.js&lt;/code&gt; 和 &lt;code&gt;$ node&lt;/code&gt; 的判断启动不同的逻辑。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// make process.argv[1] into a full path&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preloadModules&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v8debug&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execArgv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^--debug-brk&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;=&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)?&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;debugTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_DEBUG_TIMEOUT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runMain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debugTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Main entry point into most programs:&lt;/span&gt;
    &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runMain&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_forceRepl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;NativeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isatty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// REPL&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cliRepl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requireRepl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;cliRepl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createInternalRepl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;repl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  在上述节选代码的第一个 &lt;code&gt;else if&lt;/code&gt; 中，就是对 &lt;code&gt;$ node foo.js&lt;/code&gt; 这种情况进行处理了，再做完各种初始化之后，使用 &lt;code&gt;Module.runMain();&lt;/code&gt; 来运行入口代码。&lt;/p&gt;

&lt;p&gt;  第二个 &lt;code&gt;else if&lt;/code&gt; 里面就是 &lt;code&gt;$ node&lt;/code&gt; 这种情况了。&lt;/p&gt;

&lt;p&gt;  我们在终端中打开 &lt;code&gt;$ node&lt;/code&gt; 的时候，TTY 通常是关连着的，所以 &lt;code&gt;require('tty').isatty(0)&lt;/code&gt; 为 &lt;code&gt;true&lt;/code&gt;，也就是说会进到条件分支并且执行里面的 &lt;code&gt;cliRepl&lt;/code&gt; 相关代码。&lt;/p&gt;

&lt;p&gt;  我们进入到 &lt;a href="https://github.com/nodejs/node/blob/0966ab99966b7d3fbe4d7b93797fb299595fca72/lib/module.js#L490" rel="nofollow" target="_blank" title=""&gt;lib/module.js&lt;/a&gt; 看看这个 &lt;code&gt;Module.requireRepl&lt;/code&gt; 是什么东西。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requireRepl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;internal/repl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  所以我们还是得转入 &lt;a href="https://github.com/nodejs/node/blob/0966ab99966b7d3fbe4d7b93797fb299595fca72/lib/internal/repl.js#L23" rel="nofollow" target="_blank" title=""&gt;lib/internal/repl.js&lt;/a&gt; 来一探究竟。&lt;/p&gt;

&lt;p&gt;  上面在 &lt;code&gt;node.js&lt;/code&gt; 里面我们看到它执行了这个 &lt;code&gt;cliRepl&lt;/code&gt; 的 &lt;code&gt;createInternalRepl&lt;/code&gt; 函数，它的实现大概是这样的：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createRepl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ignoreUndefined&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isTTY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;useGlobal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REPL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPL_MODE_STRICT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sloppy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REPL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPL_MODE_SLOPPY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;magic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REPL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPL_MODE_MAGIC&lt;/span&gt;
  &lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_REPL_MODE&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;REPL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  转头一看这个 lib/internal/repl.js 顶端的模块引入，赫然看到一句话：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REPL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  真相大白。&lt;/p&gt;
&lt;h2 id="小结"&gt;小结&lt;/h2&gt;
&lt;p&gt;  最后再梳理一遍。&lt;/p&gt;

&lt;p&gt;  在于 Node.js 的 &lt;code&gt;vm&lt;/code&gt; 里面，顶级作用域下的 &lt;code&gt;var&lt;/code&gt; 会把变量贴到 &lt;code&gt;global&lt;/code&gt; 下面。而 REPL 使用了 &lt;code&gt;vm&lt;/code&gt;。然后 &lt;code&gt;$ node&lt;/code&gt; 进入的一个模式就是一个特定参数下面启动的一个 &lt;code&gt;REPL&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;  所以我们一开始提出的问题里面在 &lt;code&gt;$ node foo.js&lt;/code&gt; 模式下执行是 &lt;code&gt;undefined&lt;/code&gt;，因为不在全局变量上，但是启用 &lt;code&gt;$ node&lt;/code&gt; 这种 REPL 模式的时候得到的结果是 &lt;code&gt;2&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="番外"&gt;番外&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;小龙：&lt;/strong&gt;我用 node test.js 跑出来是 &lt;code&gt;a: undefined&lt;/code&gt;；那我应该怎么修改“环境”，来让他跑出：&lt;code&gt;a: 2&lt;/code&gt; 呢？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  于是有了上面写的那段代码。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;console&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runInContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var a = 2;function foo(){console.log(this.a);}foo();&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>xadillax</author>
      <pubDate>Thu, 26 Nov 2015 22:44:52 +0800</pubDate>
      <link>https://ruby-china.org/topics/28207</link>
      <guid>https://ruby-china.org/topics/28207</guid>
    </item>
    <item>
      <title>我为什么要使用哈希</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;原文（我是作者）：&lt;a href="https://xcoder.in/2015/10/16/why-i-use-hash/" rel="nofollow" target="_blank"&gt;https://xcoder.in/2015/10/16/why-i-use-hash/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="什么是哈希（Hash）"&gt;什么是哈希（Hash）&lt;/h2&gt;
&lt;p&gt;本来这里不应该出现这一节的，因为实际上大家应该都知道什么是哈希。不过有时候为了文章的完整性，我这里就稍微教条性地说明一下吧。ヽ (́◕◞౪◟◕‵) ﾉ&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;散列（英语：Hashing），通常音译作哈希，是电脑科学中一种对资料的处理方法，通过某种特定的函数、算法将要检索的项与用来检索的索引关联起来，生成一种便于搜索的数据结构。也译为散列。&lt;/p&gt;

&lt;p&gt;-- From &lt;a href="https://zh.wikipedia.org/wiki/%E6%95%A3%E5%88%97" rel="nofollow" target="_blank" title=""&gt;散列&lt;/a&gt;, Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;实际上通俗的说法就是把某种状态或者资料给映射到某个值上的操作。&lt;/p&gt;

&lt;p&gt;本酱大概就解释到这里了，至于哈希的进一步认知包括冲突的产生和解决等，如果米娜桑不了解的话还请自行学习咕。థ౪థ&lt;/p&gt;
&lt;h2 id="引子——子树问题"&gt;引子——子树问题&lt;/h2&gt;
&lt;p&gt;这个不是我在实践中遇到的问题，而是当年去某不作恶的大厂面试时候遇到的问题，觉得比较经典，所以就拿出来了。ᕙ༼ຈل͜ຈ༽ᕗ&lt;/p&gt;
&lt;h3 id="问题描述"&gt;问题描述&lt;/h3&gt;
&lt;p&gt;给定一棵二叉树，假设每个节点的数据只有左右子节点，自身并不存储数据。请找出两两完全相等的子树们。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;有兴趣的童鞋可以自己先思考一下。₍₍◝(･'ω'･)◟⁾⁾ &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="我的做法"&gt;我的做法&lt;/h3&gt;
&lt;p&gt;实际上我也不知道自己的做法是不是正确做法，不过既然通过了那一轮面试，想来也不会偏差到哪去喵。ლ（╹ε╹ლ）&lt;/p&gt;

&lt;p&gt;做法大概如下：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://zh.wikipedia.org/wiki/%E6%A0%91%E7%9A%84%E9%81%8D%E5%8E%86#.E5.90.8E.E5.BA.8F.E9.81.8D.E5.8E.86" rel="nofollow" target="_blank" title=""&gt;后序遍历&lt;/a&gt;一遍整棵树。&lt;/li&gt;
&lt;li&gt;对于遍历到每一个节点，都获取到左右子节点的哈希值，然后将其拼接重新计算出自身的哈希值，并返回给父亲节点。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;至于哈希值怎么算，方法有很多。最简单的就是设叶子节点一个哈希值，比如是 &lt;code&gt;md5("")&lt;/code&gt;，然后每次非叶子节点的哈希值就用 &lt;code&gt;md5(LEFT_HASH + RIGHT_HASH)&lt;/code&gt; 来计算。大家也可以自己随便想一种方法来做就好了。&lt;/p&gt;

&lt;p&gt;很多人可能不解了，明明是用 &lt;code&gt;md5&lt;/code&gt;，这篇文章是讲哈希，有毛线关系。(╯°O°)╯┻━┻&lt;/p&gt;

&lt;p&gt;实际上 &lt;code&gt;md5&lt;/code&gt; 就是一种哈希算法，而且是非常经典的哈希算法。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;典型的哈希算法包括 MD2、MD4、MD5 和 SHA-1 等。当然不局限于这些，对于数字来说，取模也算是哈希算法，对于字符串状态转整数状态哈希来说还有诸如 &lt;a href="http://www.partow.net/programming/hashfunctions/#BKDRHashFunction" rel="nofollow" target="_blank" title=""&gt;BKDR&lt;/a&gt;、&lt;a href="http://www.partow.net/programming/hashfunctions/#ELFHashFunction" rel="nofollow" target="_blank" title=""&gt;ELF&lt;/a&gt; 等等。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果大家想多了解一些字符串转数字哈希的算法，可以参考一下 BYVoid 的这篇《&lt;a href="https://www.byvoid.com/blog/string-hash-compare/" rel="nofollow" target="_blank" title=""&gt;各种字符串 Hash 函数比较&lt;/a&gt;》，或者想直接在 Node.js 里面使用的小伙伴们可以光顾下这个包——&lt;a href="https://github.com/XadillaX/bling_hashes" rel="nofollow" target="_blank" title=""&gt;bling-hashes&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;初步的轮廓已经明晰了，说白了就是将每个节点的哈希全算出来，如果是父亲节点就用子节点的哈希拼接起来再哈希一遍。σ`∀´)σ&lt;/p&gt;

&lt;p&gt;把这些哈希算出来之后放在一个散列表里面待查。如果一个算出来的哈希跟之前已有的哈希值相等，那么就是说这个节点跟那个节点为根节点的子树有可能完全相等。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;注意：&lt;strong&gt;有可能&lt;/strong&gt;完全相等。&lt;/p&gt;

&lt;p&gt;注意：只是&lt;strong&gt;有可能&lt;/strong&gt;完全相等。&lt;/p&gt;

&lt;p&gt;注意：重要的事情说三遍，只是&lt;strong&gt;有可能&lt;/strong&gt;完全相等。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;哈希是存在着一定的冲突概率的，所以说两个相等的哈希所检索到的源不一定一样，所以我们根据这些计算到的哈希建立哈希表，然后把表中同哈希值的子树再两两同时遍历一遍以检验是否相等。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;同时递归，取两个子树的根节点。&lt;/li&gt;
&lt;li&gt;后序遍历，看看每个节点是不是都一样存在（或者不存在）左子节点以及存在（或者不存在）右子节点。&lt;/li&gt;
&lt;li&gt;循环往复一直到两两遍历完整棵树得到验证结果。如果半路有一个节点的左右子节点状态不一样就可以直接跳出递归返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;至此为止，我们可以看出大概是两大步——&lt;strong&gt;计算各子树的哈希值&lt;/strong&gt;和&lt;strong&gt;验证各同哈希子树的相等性&lt;/strong&gt;。不过稍微变通一下，我们就可以在计算出哈希值的时候就去跟以前的对比了。&lt;/p&gt;
&lt;h4 id="剪枝"&gt;剪枝&lt;/h4&gt;
&lt;p&gt;实际上上面的做法还有一个优化的方案，不过跟哈希相关性已经基本上很小了。不过还是跟&lt;strong&gt;解决冲突&lt;/strong&gt;有一丢丢的关系的，没兴趣的童鞋也可以直接跳过了。(๑•́ ₃ •̀๑)&lt;/p&gt;

&lt;p&gt;由于子树哈希值是存在一定的冲突概率的，所以两个同哈希的子树不一定相同。那么我们如果能一眼看出这样的两棵子树是不相等的，就可以省略验证这一个递归的步骤了。&lt;/p&gt;

&lt;p&gt;这里有一种最显而易见的情况我们是可以忽略省略步骤的，那就是深度。&lt;/p&gt;

&lt;p&gt;如果两棵子树两两完全相等，那么说明这俩基佬的深度（或者说高度）是一样的，如果连深度都不一样了还如何愉快搞基——所以说如果有两个相等哈希值的子树的深度不一样的话可以直接略过验证步骤了。&lt;/p&gt;

&lt;p&gt;那么就可以这么做：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;设所有叶子节点的深度为 &lt;code&gt;0&lt;/code&gt;，然后每往上一层加一。&lt;/li&gt;
&lt;li&gt;遇到左右子节点深度不一样的父节点时，取深度大的那个子节点深度去加一。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以上步骤在遍历计算哈希的时候顺便也做了，这样就多了一个验证标记了。&lt;/p&gt;

&lt;p&gt;所以差不多就这样了，浅尝辄止。( ˘･з･)&lt;/p&gt;
&lt;h3 id="引子的小结"&gt;引子的小结&lt;/h3&gt;
&lt;p&gt;就上述的场景来说，哈希非常好地将一个非常复杂的状态转化成一个可以检索的状态。本来毫无头绪的一个问题使用了哈希之后就完全变成了一个检索加验证的过程了。&lt;/p&gt;
&lt;h2 id="报告图问题"&gt;报告图问题&lt;/h2&gt;
&lt;p&gt;这个问题就是我在大搜车中确实遇到的场景了。大家也不需要知道什么是报告图，就当它是一个代号了。&lt;/p&gt;
&lt;h3 id="问题描述"&gt;问题描述&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;要做的事情大概就是说给定一个报告，我们根据报告的各个细节选定各种图层然后揉成一团叠加在一起形成最后一个结果图。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;其实本来就有个系统在做这件事情的——每来一个报告就生成一张图，然后存储好之后给前端使用。&lt;/p&gt;

&lt;p&gt;我做的事情是将逻辑迁移到另一套计算密集型任务集中处理系统中去。(&lt;em&gt;´艸`&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;其实生成这样一张图片的逻辑是 CPU 计算密集型的逻辑，所以比较耗费资源和时间的，那么我们就能在这上面做点手脚优化一下。&lt;/p&gt;
&lt;h3 id="优化方法"&gt;优化方法&lt;/h3&gt;
&lt;p&gt;首先我们要知道的是，有哪些图层是固定的，所以其实这算半个排列组合的问题了。&lt;/p&gt;

&lt;p&gt;不过我们也知道排列组合的增长性非常快，更何况我这里有约 100 个图层选择，所以可能性非常多，一下子全生成好不可能。&lt;/p&gt;

&lt;p&gt;那么就可以用哈希和懒惰的思想来实现了。(&lt;em&gt;ˇωˇ&lt;/em&gt;人)&lt;/p&gt;

&lt;p&gt;虽然报告是有无限种可能的，但是把报告转成图层数据之后，拥有完全一样的图层数据的报告就可以用同一张图片了，这样就可以大大节省空间和时间了。&lt;/p&gt;

&lt;p&gt;其实大概的步骤非常简单：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;把图层数据计算成哈希。（比如把所有图层文件路径用某种符号拼接，再用 &lt;code&gt;md5&lt;/code&gt; 计算一下）&lt;/li&gt;
&lt;li&gt;去数据库查找这个哈希主键存不存在。

&lt;ul&gt;
&lt;li&gt;如果存在则验证源图层数据域当前图层数据是否吻合。

&lt;ul&gt;
&lt;li&gt;如果不吻合则按某种算法重新计算哈希，继续步骤 2。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如果吻合则可以直接拿着这个数据返回了，跳出计算。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果不存在就说明当前数据库还没有这个图层情况的报告图生成，那么就执行生成报告图逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;报告图生成之后，将其存入数据库中。

&lt;ul&gt;
&lt;li&gt;计算出这个报告图图层数据的哈希，去数据库查存不存在。

&lt;ul&gt;
&lt;li&gt;如果不存在则说明哈希不冲突，能用，直接用这个哈希存进去。&lt;/li&gt;
&lt;li&gt;如果存在则说明哈希冲突，那么按某种算法重新计算哈希，继续上面的步骤直到不冲突为止。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果大家想知道“按某种算法重新生成哈希”里面“某种算法”的话可以看看下面的瞎狗眼的说明了。(ﾉ◕ヮ◕) ﾉ*:･ﾟ✧&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;其实很简单，把图层数据的这个字符串加某个固定字符当小尾巴，如果哈希还是冲突则继续加这个小尾巴，直到计算出来的哈希不冲突为止。&lt;/p&gt;

&lt;p&gt;比如我就用了这字符当小尾巴——&lt;strong&gt;🀣&lt;/strong&gt;（麻将牌中的蘭）。(♛‿♛)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="报告图的小结"&gt;报告图的小结&lt;/h3&gt;
&lt;p&gt;在这种场景中，我把哈希拿来作检索某种报告图是否已经生成的用途。如果没有生成则生成一张，如果已经生成则直接拿已有的报告图去用。&lt;/p&gt;

&lt;p&gt;至少比原来的来一张报告就生成一张图片来得快，并且省空间——相当于作冗余处理了。&lt;/p&gt;

&lt;p&gt;事实上在很多的网盘系统中也有作冗余处理的。你以为你有多少多少 T 的空间，实际上相同的文件最终在网盘系统里面只存一份（不过排除备份的那些），而我相信做这些冗余判断的原理就是哈希了，SHA-1 也好 MD5 也好，反正就是这样。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;上面网盘的冗余处理原理也只是我的猜测，我没在那些厂子里面工作过所以不能说就是就是这样子的。欢迎指正。｡ﾟヽ (ﾟ´Д`) ﾉﾟ｡&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="唯一主键问题"&gt;唯一主键问题&lt;/h2&gt;
&lt;p&gt;这是我来这边工作后的另一个小插曲了，遇到一个主键生成的小需求。&lt;/p&gt;
&lt;h3 id="问题描述"&gt;问题描述&lt;/h3&gt;
&lt;p&gt;有一个数据要插入到数据库，所以要给它生成一个主键，但是需求比较奇葩，可能是历史遗留问题吧。(눈‸눈)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;非自增。&lt;/li&gt;
&lt;li&gt;是一个全是数字的字符串。&lt;/li&gt;
&lt;li&gt;不同类型的这个表的数据用不同的前缀，比如 &lt;code&gt;10&lt;/code&gt;、&lt;code&gt;11&lt;/code&gt;、&lt;code&gt;12&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;位数在十几位左右（不过在我这里就固定了）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="解决方案"&gt;解决方案&lt;/h3&gt;
&lt;p&gt;如果是 &lt;code&gt;前缀 + 随机数&lt;/code&gt; 的冲突概率会比较大的，所以还是用哈希来搞。&lt;/p&gt;

&lt;p&gt;非常简单。首先前缀是固定的，我们就不管了，然后我根据这次进来的数据拼接成字符串（数据不会完全一样的），加上一点随机盐，然后用字符串哈希计算一遍，加上前导零，加上当前时间戳的后几位拼接起来，最后接上前缀就好了。&lt;/p&gt;

&lt;p&gt;这个 &lt;code&gt;generate&lt;/code&gt; 函数看起来就像这样子：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bling-hashes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bodyParamStr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;basePrefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;basePrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;basePrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;11&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bkdr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParamStr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;millisecond&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;basePrefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;这里的 &lt;code&gt;bling&lt;/code&gt; 就是上面提到过的那个 &lt;a href="https://github.com/XadillaX/bling_hashes" rel="nofollow" target="_blank" title=""&gt;bling-hashes&lt;/a&gt;，采用了 &lt;code&gt;BKDR&lt;/code&gt; 算法来计算哈希。以及 &lt;code&gt;Number.prototype.pad&lt;/code&gt; 函数是我邪恶得使用了 &lt;a href="http://sugarjs.com/api/Number/pad" rel="nofollow" target="_blank" title=""&gt;SugarJs&lt;/a&gt; 里面的函数，就是加上前导零的意思。如果受“千万不要修改原型链”影响较深地童鞋别学我哦。&lt;code&gt;bodyParamStr&lt;/code&gt; 是前端传过来的 &lt;strong&gt;Raw Form Data&lt;/strong&gt;，它看起来像 &lt;code&gt;"data1=1&amp;amp;data2=2&amp;amp;..."&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;最后得到的这个字符串是我们所要的主键了。｡:.ﾟヽ (*´∀`) ﾉﾟ.:｡&lt;/p&gt;

&lt;p&gt;不过要注意的是，这个主键仍然又冲突的可能性，所以一旦冲突了（无论是自己检测到的还是插入数据库的时候疼了）就需要再生产一遍。就目前来说再生成的时候毫秒时间戳后三位会不一样，所以问题不大，允许存在的误差——毕竟不是那种分分钟集千万条的数据，肯定在 &lt;code&gt;int&lt;/code&gt; 范围内。如果到时候真出问题了再改进。&lt;/p&gt;
&lt;h3 id="主键的小结"&gt;主键的小结&lt;/h3&gt;
&lt;p&gt;这里的哈希是用在生成基本上没有碰撞的主键身上，感觉效果也是非常不错的——前提是你也有这种奇葩需求。&lt;/p&gt;
&lt;h2 id="真·小结"&gt;真·小结&lt;/h2&gt;
&lt;p&gt;本文大致介绍了哈希的几种用途，有可能是大家熟知的用途，也有可能是巧用，总之就是说了为什么我要用哈希。&lt;/p&gt;

&lt;p&gt;在编程中，无论是实际用途还是自己玩玩的题目，多动动脑子就会出来一些“奇技淫巧”。哈希也好，别的东西也罢，反正都是为了解决问题的——千万别因为实际开发中通常性的“并没有什么卵用”而去忽视它们，虽然哈希已经是够常用的了。(๑•ૅω•´๑)&lt;/p&gt;</description>
      <author>xadillax</author>
      <pubDate>Fri, 16 Oct 2015 15:06:03 +0800</pubDate>
      <link>https://ruby-china.org/topics/27700</link>
      <guid>https://ruby-china.org/topics/27700</guid>
    </item>
  </channel>
</rss>
