CommonJS Require Mechanics

huangapple go评论68阅读模式
英文:

CommonJS Require Mechanics

问题

根据我的教材,require 函数的定义如下:

require.cache = Object.create(null);

function require(name) {
    if (!(name in require.cache)) {
        let code = readFile(name);
        let module = {exports: {}};
        require.cache[name] = module;
        let wrapper = Function("require, exports, module", code);
        wrapper(require, module.exports, module);
    }
    return require.cache[name].exports;
}

注意事项:

  • readFile(name) 是一个取决于是否使用 Node.js 或特定浏览器的任意函数。
  • 我们将缓存设置为空对象。
  • 这是我的教材引用的代码摘录。

为了避免多次加载相同的模块,require 会保持一个已加载模块的存储(缓存)。当调用时,它首先检查所请求的模块是否已加载,如果没有,则加载它。这涉及读取模块的代码,将其包装在函数中,并调用它。

关于您不理解的部分:
在这种情况下,递归是如何工作的呢?我真的很困惑包装函数。

英文:

According to my textbook, the definition of the require function is this

require.cache = Object.create(null);

function require(name) {
    if (!(name in require.cache)) {
        let code = readFile(name);
        let module = {exports: {}};
        require.cache[name] = module;
        let wrapper = Function("require, exports, module", code);
        wrapper(require, module.exports, module);
    }
    return require.cache[name].exports;
}

Notes:

  • readFile(name) is an arbitrary function that depends on whether I am using node.js or a particular browser.
  • We set the cache to be an empty object
  • This is what my textbook quoted about the code.

> To avoid loading the same module multiple times, require keeps a store
> (cache) of already loaded modules. When called, it first checks if the
> requested module has been loaded and, if not, loads it. This involves
> reading the module’s code, wrapping it in a function, and calling it.

What I do not understand:
How does the recursion work in this case. I am just really confused about the wrapper function.

答案1

得分: 1

请找到以下已翻译的部分:

假设您有这些非常简单的模块<sup>1</sup>:

// In square.js
const square = function (n) {
  return n * n;
}

module .exports = square

// In squareAll.js
const square = require ('./square')

const squareAll = function (ns) {
  return ns .map (n => square (n))
}

module .exports = squareAll

与此主模块:

const squareAll = require ('./squareAll')

console .log (squareAll ([1, 2, 3, 4, 5]))

在外部的 require ('./squareAll') 上,当我们到达以下行时:

let wrapper = Function("require, exports, module", code);

我们创建了一个新的函数,看起来像这样:

const wrapper = function (require, exports, module) {
  const square = require ('./square')

  const squareAll = function (ns) {
    return ns .map (n => square (n))
  }

  module .exports = squareAll
}

我们调用这个构造函数,传递我们的 require 函数、我们的 module 对象和它的 exports 属性。然后,当其第一行被调用时,将递归调用 require

const square = require ('./square')

这将再次使用函数构造函数,调用那个函数,然后将 square 函数返回,并将其分配给 square 变量。回到外部调用,我们将使用 square 函数来定义 squareAll,将其分配给 module.exports,然后使用以下方式返回它:

return require.cache[name].exports;

我们的 index.js 函数现在具有对 squareAll 的引用,它具有对 square 的引用。由于缓存的存在,下次我们 require 其中一个时,将返回已加载的版本。

这是一个相当优雅的过程。


<sup><sup>1</sup>是的,这些可以使用箭头函数更简单地编写。我这样做是为了使尽可能多的人能够理解。</sup>

英文:

Imagine you had these extremely simple modules<sup>1</sup>:

// In square.js
const square = function (n) {
  return n * n;
}

module .exports = square

and

// In squareAll.js
const square = require (&#39;./square&#39;)

const squareAll = function (ns) {
  return ns .map (n =&gt; square (n))
}

module .exports = squareAll

with this main module:

const squareAll = require (&#39;./squareAll&#39;)

console .log (squareAll ([1, 2, 3, 4, 5]))

On that outer require (&#39;./squareAll&#39;), when we get to the line

let wrapper = Function(&quot;require, exports, module&quot;, code);

we create a new Function that looks something like this:

const wrapper = function (require, exports, module) {
  const square = require (&#39;./square&#39;)

  const squareAll = function (ns) {
    return ns .map (n =&gt; square (n))
  }

  module .exports = squareAll
}

We call this constructed function passing our require function, our module object and its exports property. That will then recursively call require when its first line is called:

const square = require (&#39;./square&#39;)

and that will use the Function constructor again, invoke that function, and will return the square function, assigning it to the square variable. Back in the outer call, we will use the square function to define squareAll, assign that to module.exports, and then we will return it with:

return require.cache[name].exports;

Our index.js function now has a reference to squareAll, which has a reference to square. And because of the caching, the next time we require one of them, we will return the already-loaded version.

It's a fairly elegant process.


<sup><sup>1</sup>Yes, these can be written more simply with arrow function. I do, but wanted to make this understandable to as many as possible.</sup>

huangapple
  • 本文由 发表于 2023年5月26日 10:01:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76337217.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定