You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

《Eloquent JavaScript》第10章module.exports相关技术疑问

嘿,让我结合这段手写的CommonJS风格require实现,来拆解你的两个问题:

首先先贴出你给出的代码:

function require(name) { 
  if (name in require.cache) return require.cache[name]; 
  var code = new Function("exports, module", readFile(name)); 
  var exports = {}, module = {exports: exports}; 
  code(exports, module); 
  require.cache[name] = module.exports; 
  return module.exports; 
} 
require.cache = Object.create(null);

1. require.cache的作用是什么?

require.cache在这里扮演的是模块缓存容器的角色,核心作用有两个:

  • 避免重复执行模块代码:当你第一次调用require(name)加载某个模块时,代码会执行模块的逻辑,把最终导出的结果存在require.cache[name]里。之后再调用require加载同一个模块时,会直接返回缓存里的结果,不用再重新读取文件、执行模块代码,大大提升性能。
  • 保证模块单例性:同一个模块在整个应用中只会被初始化一次,模块内部的状态(比如定义的变量、创建的实例)会被共享,不会因为重复加载而被重置。

2. 为什么说exports无法存储函数这类值?exports和module.exports的区别是什么?

其实这句话的准确表述应该是:不能直接给exports赋值为函数(或其他非对象类型),而不是说不能把函数挂载到exports上。我们结合代码里的逻辑来分析:

在require函数内部,一开始做了这一步:

var exports = {}, module = {exports: exports};

这时候exports是module.exports的引用——它们指向同一个空对象。

两种不同的导出场景:

  • 场景1:给exports挂载属性(包括函数)
    比如模块代码写:

    exports.sayHello = function() { console.log('Hello!') };
    

    这时候你是在修改exports和module.exports共同指向的那个对象,最后module.exports会包含sayHello函数,缓存和返回的就是这个带函数的对象,完全没问题。

  • 场景2:直接给exports赋值为函数
    比如模块代码写:

    exports = function() { console.log('Hello!') };
    

    这时候你把exports变量指向了一个新的函数对象,但module.exports还是原来的那个空对象。而require函数最后缓存和返回的是module.exports,所以你的函数根本不会被导出,外部拿到的还是空对象。

而module.exports则可以直接赋值为函数,因为最后返回的就是它:

// 模块代码
module.exports = function() { console.log('Hello!') };

这时候module.exports本身就是这个函数,会被正确缓存和返回,外部require后就能拿到这个函数。

原书作者提到的“不能将函数导出到该变量”,本质上是提醒开发者不要犯直接给exports赋值的错误,而不是说exports不能挂载函数。


内容的提问来源于stack exchange,提问作者Vadim Anisimov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:18:51