我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

苏南大叔再来描述commonjs的困惑二,require语句的缓存现象。首先澄清一下,require语句缓存现象并非是commonjs所特有的。其次,多次require导致缓存现象,也并不是太常见的,并不是大范围的问题。

苏南大叔:Commonjs困惑二:如何理解require缓存?如何删除? - commonjs-cache
Commonjs困惑二:如何理解require缓存?如何删除?(图3-1)

本文测试环境:node。本文主要议题是:require语句的缓存是什么,如何处理这种缓存。

require语句缓存是什么?

这个require语句缓存,是由node自身的机制所引起的,并非commonjs所特有的。主要表现在:在同一个文件里面,多次引用同一个模块的话,实际上除了第一次引用之外,其它引用都是缓存,读到的变量都很可能是个错误的值。
下面有个例子:
module.js:

var name = "苏南";
var age = 18;
var getAge = function () {
  return age;
};
module.exports = {
  name: name,
  getAge: getAge,
};

test.js:

var a = require("./module")
console.log(a.name)
a.name="苏南大叔"
console.log(a.name)
var a2 = require("./module")  // 同一个模块
console.log(a2.name)

在这个例子中,可以看到第二次引用module得到的a2,它的name属性居然是a中的name,随着aname变化而变化。明明aa2是不同的实例啊,但是由于require缓存的存在,这个事情就变得扑朔迷离了。按理说,完全不相同的两个实例,变成了同一个实例。

苏南大叔:Commonjs困惑二:如何理解require缓存?如何删除? - require-cache
Commonjs困惑二:如何理解require缓存?如何删除?(图3-2)

如何删除require语句缓存

这里说一下require语句缓存的破解之法,下面的是个例子:

var a = require("./module")
console.log(a.name)
a.name="苏南大叔"
console.log(a.name)
delete require.cache[require.resolve("./module")]
var a2 = require("./module2")
console.log(a2.name)

其中核心代码是:

delete require.cache[require.resolve("./module")]

这个核心代码,所需要的参数是个绝对路径。但是,一般来说,大家写的都是相对路径,所以,这里需要个函数进行一下转化。

require.resolve()

还是,值得一提的是,这个参数还是个数组。所以,是可以批量传入多个要删除缓存的文件路径的,例如:

delete require.cache[require.resolve("./module"),require.resolve("./module2")]

最后,如果您需要一个简单有效的大范围攻击魔法,下面的代码是您需要的,并不需要制定路径。直接删除所有的require缓存。

Object.keys(require.cache).forEach(function(key) {
   delete require.cache[key];
})

苏南大叔:Commonjs困惑二:如何理解require缓存?如何删除? - require-cache-clear
Commonjs困惑二:如何理解require缓存?如何删除?(图3-3)

相关链接

总结

苏南大叔个人认为:一般来说,大家并不会在同一个文件里面多次require同一个模块。但是,并不排除这种可能性,如果真的这么做了,就要考虑到一个缓存的机制,需要在适当的时机,删除这个require缓存,否则,就会出现认知上的偏差。

更多commonjs文章,请参考苏南大叔的博客:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   js    commonjs