请教JavaScript打包器工作原理及一段require函数代码解析
嘿,我来帮你拆解这段代码——它其实是打包器(比如Webpack、Browserify)实现模块加载核心逻辑的极简缩影,咱们一步步来理清楚:
首先得明确这段代码依赖的两个全局变量的作用:
modules:这是一个模块注册表,键是模块名称/ID,值是该模块的定义函数。这个函数会接收一个exports对象,模块里要对外暴露的内容都会挂载到这个对象上。dependencies:这是一个模块缓存池,用来存储已经加载执行过的模块的导出结果,避免重复加载同一个模块。
接下来逐行解析函数逻辑:
函数入口
var require = function (module) {这个
require就是我们日常导入模块时用的函数,参数module是你要加载的模块的名称或唯一ID。缓存检查
if (!dependencies[module]) {先判断目标模块是否已经被加载过:如果
dependencies里没有这个模块的记录,说明模块还未加载,进入加载流程;如果有记录,直接跳过加载步骤,返回缓存的结果。创建导出容器
var exports = {}新建一个空的
exports对象,这个对象就是模块用来向外“公开”内容的容器——模块内部的代码会把要暴露的变量、方法挂载到这个对象上。执行模块定义
modules[module](exports)从
modules注册表中取出对应模块的定义函数,把刚创建的exports对象传进去执行。比如模块里写了exports.add = (a,b) => a+b,执行后exports就会带上add方法。缓存模块结果
dependencies[module] = exports把已经填充好内容的
exports存入dependencies缓存池,这样下次再调用require加载同一个模块时,直接复用缓存的结果,不会重复执行模块代码——这就是模块的单例特性,一个模块只会被加载执行一次。返回导出内容
return dependencies[module]不管模块是刚加载的还是来自缓存,最终返回该模块的导出结果,这样调用
require的地方就能拿到模块暴露的内容了。
举个简单的例子帮你理解
假设我们有打包后的模块集合和缓存对象:
// 模拟打包后的模块注册表 var modules = { 'utils/math': function(exports) { // 模块内部把要暴露的方法挂到exports上 exports.add = function(a, b) { return a + b; }; } }; // 模块缓存池 var dependencies = {};
第一次调用require('utils/math')时:
- 因为
dependencies['utils/math']不存在,会创建空exports,执行modules['utils/math'](exports),让exports带上add方法; - 然后把这个
exports存入缓存; - 最后返回这个
exports,你就能用math.add(1,2)得到结果3。
第二次再调用require('utils/math')时,直接从dependencies里取出缓存的exports返回,不会再执行模块里的代码。
内容的提问来源于stack exchange,提问作者Wayne Zhao

