【问题标题】:How does require() in node.js work?node.js 中的 require() 是如何工作的?
【发布时间】:2012-03-17 13:29:07
【问题描述】:

我试过了:

// mod.js
var a = 1;
this.b = 2;
exports.c = 3;

// test.js
var mod = require('./mod.js');
console.log(mod.a);    // undefined
console.log(mod.b);    // 2
console.log(mod.c);    // 3, so this === exports?

所以我认为 require() 可能是这样实现的:

var require = function (file) {
    var exports = {};
    var run = function (file) {
        // include "file" here and run
    };
    run.apply(exports, [file]);
    return exports;
}

对吗?请帮助我理解 require(),或者我在哪里可以找到源代码。谢谢!

【问题讨论】:

    标签: javascript node.js this require apply


    【解决方案1】:

    源代码为hereexports/require 不是关键字,而是全局变量。您的主脚本是 wrapped 之前的 start 在一个函数中,该函数在其上下文中包含所有全局变量,如 requireprocess 等。

    请注意,虽然 module.js 本身使用的是 require(),但这是一个不同的 require 函数,它在名为“node.js”的文件中是 defined

    上面的副作用:在模块中间有“return”语句(不属于任何函数)是非常好的,有效地“注释掉”其余代码

    【讨论】:

    • 这并没有让它变得更简单。该模块使用require,同时还定义了require。仅考虑到源代码,我觉得这一步有点难以理解。
    • 模块本身需要一个不同的require。创建模块的简化版本来引导模块系统 - 看看这里的代码 - github.com/nodejs/node/blob/v4.0.0/src/node.js#L861-L949
    • 这些全局变量及其返回值的文档在哪里?
    • @Srikan 在官方文档中 - nodejs.org/dist/latest-v8.x/docs/api/… (我对调用导出/需要全局变量并不完全正确 - 通常它们是加载模块时调用的包装函数的参数)
    • @AlexanderMills 它不完全是全局变量,这是因为每个模块都包装在一个函数中,require 作为参数之一传递给该函数
    【解决方案2】:
    var mod = require('./mod.js');
    

    require 是一个函数,它接受一个名为 path 的参数,在这种情况下,路径是 ./mod.js

    当调用 require 时,会发生一系列任务:

    1. 调用lib/module.js 中声明的Module.prototype.require 函数,该函数断言路径存在并且是一个字符串

    2. 调用Module._load,这是lib/module.js中的一个函数,通过Module._resolveFilename(request, parent, isMain)解析文件,

    3. 调用Module._resolveFilename函数并检查模块是否是本地的(本地模块由NativeModulelib/internal/bootstrap_node.js中定义的函数返回), 如果是,它将返回模块,否则它会检查 parh 的字符数(至少必须 2 个字符)和一些字符(路径必须以 ./ 开头) 通过在lib/internal/bootstrap_node.js 中定义的Module._resolveLookupPaths 函数
    4. 检查包含文件的目录
    5. 如果路径包含扩展名(在我们的示例中是:mod.js),lib/path.js 中定义的基本名称函数会检查扩展名是否为“js
    6. 然后它将为参数var module = new Module(filename, parent);中给出的文件创建一个新模块
    7. 内容将通过lib/internal/bootstrap_node.js中定义的函数NativeModule.prototype.compile通过v8编译
    8. lib/internal/bootstrap_node.js 中定义的NativeModule.wrap 采用mod.js 编译的javascript 内容并将其包装:它将其包装在其他代码中,使所有这些工作。 因此,您在mod.js 中编写的代码被包装在一个函数表达式中。这意味着您在 node 中编写的所有内容都在 V8 中运行
    9. 返回的是 module.exports

    【讨论】:

      【解决方案3】:

      Andrey 展示了源代码,但如果您也想知道如何使用它,这里有简单明了的解释(http://nodejs.org/api/modules.html)。

      这对我来说是两个很好的例子。

      //foo.js, multiple methods
      var circle = require('./circle.js');
      console.log( 'The area of a circle of radius 4 is ' + circle.area(4));
      
      //circle.js
      var PI = Math.PI;
      exports.area = function (r) {
        return PI * r * r;
      };
      exports.circumference = function (r) {
        return 2 * PI * r;
      };
      
      //bar.js
      var square = require('./square.js');
      var mySquare = square(2);
      console.log('The area of my square is ' + mySquare.area());
      
      //square.js, single method
      module.exports = function(width) {
        return {
          area: function() {
            return width * width;
          }
        };
      }
      

      我最喜欢的模式是

      (function (controller) {
      
        controller.init = function (app) {
      
          app.get("/", function (req, res) {
              res.render("index", {});
          });
      
        };
      })(module.exports);
      

      【讨论】:

      • 如果定义一个var express = require('express'),为什么在它之后,他们必须重新定义另一个变量为var app = express()
      • 不明白你最喜欢的模式与require有什么关系
      • @TomSawyer,因为require('express') 返回一个返回应用程序的函数。这只是他们建造它的方式。希望自从您 4 年前提出这个问题以来,您已经回答了它。
      【解决方案4】:

      我挖掘了一点nodejs源代码/2/并制作了一个序列图/1/,希望这能给你一个直观的概述。还有一篇文章http://fredkschott.com/post/2014/06/require-and-the-module-system/也简单的解释了require()机制,先看这篇文章可以帮助你快速理解这个图。

      参考:

      /1/ 图源repo:https://github.com/z1yuan/nodejs.git

      /2/https://github.com/nodejs/node-v0.x-archive.git

      【讨论】:

        【解决方案5】:

        试试这个。
        这是我用来创建与 Node.js 相同功能的 sn-p

        /*
        FILE: require.js
        */
        /*
        This is the file used
        */
        window.require = function(src, ret) {
          if (src === 'jsmediatags') {
            src = 'https://cdnjs.cloudflare.com/ajax/libs/jsmediatags/3.9.5/jsmediatags.js';
          };
          var d = document.createElement('script');
          d.src = src;
          document.head.appendChild(d);
          var fullURL = src.split('://');
          var neededURL = fullURL[1];
          var nameParts = neededURL.split('/');
          var nameNUM = nameParts.length - 1;
          var fileName = nameParts[nameNUM];
          var g = fileName.split('.');
          var global = g[0];
          if (ret === true) {
            return window[global]
          };
        };
        看看这是否有效,并将更多文件添加到其库中,只需输入更多。 (if (src===yourfilenamehere) { src = "path/to/your/file" }

        【讨论】:

        • 这没有回答 OP 的要求。 OP 询问 require 功能如何工作以及如何实现。这个解决方案是如何用纯JavaScript重新创建node.jsrequire函数。
        【解决方案6】:

        在下载旁边可以找到源代码:http://nodejs.org/exports/require 是关键字,我不认为它们是直接用 javascript 编码的。 Node 是用 C++ 编写的,而 javascript 只是一个围绕 C++ 核心的脚本外壳。

        【讨论】:

        • 当您只是“思考”或猜测时,最好不要回答问题。当一个模块从文件系统加载和解析时,它被包装在一个函数中,并由 v8 引擎编译,最后模块被缓存。 requiremodule__filename等是编译后注入模块的函数和变量,模块运行在v8引擎上下文中,但是模块本身是一个闭包,所以变量和函数永远不会冲突(除非你使用全局变量和混乱。
        猜你喜欢
        • 2012-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-18
        • 2020-10-10
        • 2018-06-22
        相关资源
        最近更新 更多