Eloquent Javascript 需要功能说明

Eloquent Javascript require function explanation needed

作者解释了 require 函数 here 的最小实现,如下所示:

function require(name) {
  var code = new Function("exports", readFile(name));
  var exports = {};
  code(exports);
  return exports;
}

console.log(require("weekDay").name(1));
// → Monday

我很难理解到底发生了什么,主要是因为示例不完整。现在,我已经接受 readFile() returns 字符串格式的代码。真正让我困惑的部分是:为什么要将 var exports 传递到代码中,它在做什么,为什么要返回它? 如何 exports返回 readFile 检索到的代码的对象?

此示例需要了解函数构造函数。

这可能有帮助:

$ node
> f = new Function("x", "return x + 2");
[Function]
> f(8)
10

在您的示例中,函数的主体是文件中的代码,您将在其中看到对变量 exports 属性的赋值。那么什么是exports呢?它是一个最初为空的对象。模块的主体在调用时将 "fill it in"。在调用 code(exports) 之后,您将得到一个填充的对象,里面有很多好东西,它从 require.

返回

示例:

假设文件包含

var x = 3
exports.y = 10 * x

然后你的调用将传入一个空对象,但执行函数会将 y 属性 添加到对象中,你将返回

{y: 30}

new Function("exports", body) 将创建一个匿名函数,它接受一个参数 (exports) 并执行在 body 中找到的代码。 将被传递到函数体中的 exports 开始时是一个空对象,目的是模块体将用它想要向世界其他地方公开的东西填充它。然后函数被执行(我们传入 exports 对象)。最后返回模块导出的东西

这是一个更完整的例子:

fakeFileSystem = {
  "weekDay.js": "                \
      var days = [               \
        'Sunday',                \
        'Monday',                \
        'Tuesday',               \
        'Wednesday',             \
        'Thursday',              \
        'Friday',                \
        'Saturday'               \
      ];                         \
      function name(dayNo) {     \
        return days[dayNo];      \
      }                          \
      exports.name = name;       \
  "
};

function require(name) {
  var code = new Function("exports", fakeFileSystem[name + ".js"]);
  var exports = {};
  code(exports);
  return exports;
}

console.log(require("weekDay").name(1));
// → Monday

这是有效的,因为 code 的构造就像是这个函数:

function(exports) {
  var days = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday'
  ];
  function name(dayNo) {
    return days[dayNo];
  }
  exports.name = name;
}

我们把{}传入这个函数,它会修改为

{
  name: function(dayNo) {
    return days[dayNo];
  }
}

其中 days 在闭包中被捕获,但对外界不可见。这允许我们只访问模块中明确添加到 exports 的内容(如 name),同时隐藏所有未添加的内容(如 days)。