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

IT技术 javascript node.js this require apply
2021-03-19 14:45:16

我试过这个:

// 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(),或者我在哪里可以找到源代码。谢谢!

6个回答

源代码在这里exports/require不是关键字,而是全局变量。您的主脚本开始之前包装在一个函数中,该函数在其上下文中具有所有全局变量,例如,等。requireprocess

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

上面的副作用:在你的module(不属于任何函数)中间有“return”语句是完全没问题的,有效地“注释掉”其余的代码

module本身中的require是一个不同的requiremodule的简化版本被创建来引导module系统 - 看这里的代码 - github.com/nodejs/node/blob/v4.0.0/src/node.js#L861-L949
2021-04-22 14:45:16
这些全局变量及其返回值的文档在哪里?
2021-05-02 14:45:16
@AlexanderMills 它不完全是全局变量,它来自这样一个事实,即每个module都包含在一个函数中并require作为参数之一传递给该函数
2021-05-14 14:45:16
这并没有让它变得更简单。该module使用requirewhile 它还定义了require. 仅考虑到源代码,我发现这是一个有点难以理解的举动。
2021-05-18 14:45:16
官方文档中的@Srikan - nodejs.org/dist/latest-v8.x/docs/api/... (我对调用exports/require 全局变量并不完全正确 - 通常它们是被调用的包装函数的参数当您的module加载时)
2021-05-18 14:45:16
var mod = require('./mod.js');

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

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

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

  2. callModule._loadlib/module.js中的一个函数,它通过解析文件Module._resolveFilename(request, parent, isMain)

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

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);
@TomSawyer,因为require('express')返回一个返回应用程序的函数。这只是他们建造它的方式。希望自从您 4 年前问这个问题以来,您已经回答了它。
2021-05-04 14:45:16
如果定义 a var express = require('express'),为什么在它之后,他们必须将另一个变量重新定义为var app = express()
2021-05-11 14:45:16
不知道你最喜欢的模式与 require 有什么关系
2021-05-19 14:45:16

挖了一点nodejs源码/2/,做了一个时序图/1/,希望能给大家一个直观的概览。还有另一篇文章http://fredkschott.com/post/2014/06/require-and-the-module-system/也用简单的方式解释了 require() 机制,先通读这篇文章可以帮助你快速理解图表。 在此处输入图片说明

参考:

/1/ 图源代码库:https : //github.com/z1yuan/nodejs.git

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

试试这个。
这是我用来创建与 Node.js 相同的功能的片段

/*
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功能如何工作以及如何实现。这个解决方案是如何node.js require用纯 JavaScript重新创建函数。
2021-04-24 14:45:16