node.js 中的 require() 是如何工作的?

Posted

技术标签:

【中文标题】node.js 中的 require() 是如何工作的?【英文标题】:How does require() in node.js work? 【发布时间】: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(),或者我在哪里可以找到源代码。谢谢!

【问题讨论】:

【参考方案1】:

源代码为here。 exports/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 时,会发生一系列任务:

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

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

    调用Module._resolveFilename函数并检查模块是否是本地的(本地模块由NativeModule在lib/internal/bootstrap_node.js中定义的函数返回), 如果是,它将返回模块,否则它会检查 parh 的字符数(至少必须 2 个字符)和一些字符(路径必须以 ./ 开头) 通过在lib/internal/bootstrap_node.js 中定义的Module._resolveLookupPaths 函数 检查包含文件的目录 如果路径包含扩展名(在我们的示例中是:mod.js),lib/path.js 中定义的基本名称函数会检查扩展名是否为“js” 然后它将为参数var module = new Module(filename, parent);中给出的文件创建一个新模块 内容将通过lib/internal/bootstrap_node.js中定义的函数NativeModule.prototype.compile通过v8编译 在lib/internal/bootstrap_node.js 中定义的NativeModule.wrap 采用mod.js 编译的javascript 内容并将其包装:它将其包装在其他代码中,使所有这些工作。 因此,您在mod.js 中编写的代码被包装在一个函数表达式中。这意味着您在 node 中编写的所有内容都在 V8 中运行 返回的是 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引擎上下文中,但是模块本身是一个闭包,所以变量和函数永远不会冲突(除非你使用全局变量和混乱。

以上是关于node.js 中的 require() 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

node.js 中的 module.parent 有啥用?如何引用 require()ing 模块?

Node.js:如何将全局变量传递到通过 require() 插入的文件中?

Node.js 'require' 语句中的大括号(大括号)

node.js 中的 require('dotenv').config()

Node.js中require()单例

如何解决node.js中的“消息内容必须是非空字符串”[关闭]