面试题答案
一键面试模块加载流程
- 内置模块
- 查找顺序:Node.js优先加载内置模块。例如
http
、fs
等模块,只要在代码中使用require('http')
等形式,Node.js会直接从其内部模块列表中加载,无需在文件系统中查找。 - 加载方式:内置模块是在Node.js编译过程中就已经被编译进可执行文件,加载速度极快。当使用
require
调用时,直接从内存中获取。
- 查找顺序:Node.js优先加载内置模块。例如
- 自定义模块
- 查找顺序:如果模块标识符不是以
/
、./
、../
开头(表示非文件系统路径),Node.js会按照当前目录为基础进行查找。首先会在当前目录下查找同名的文件或目录。如果是文件,会按.js
、.json
、.node
的顺序尝试加载(.js
文件会被解释执行,.json
文件会被解析为JSON对象,.node
文件是C++ 插件,会被加载执行)。如果是目录,会查找目录下的package.json
文件中的main
字段指定的入口文件,如果没有package.json
文件或main
字段未指定,会查找目录下的index.js
、index.json
、index.node
文件。 - 加载方式:对于
.js
文件,Node.js会使用fs
模块读取文件内容,并使用vm
模块将其编译执行。对于.json
文件,使用fs
模块读取后,通过JSON.parse
解析为JavaScript对象。对于.node
文件,会使用process.dlopen
加载动态链接库。
- 查找顺序:如果模块标识符不是以
- 第三方模块
- 查找顺序:Node.js会从当前模块所在目录开始,向上级目录查找
node_modules
目录,直到根目录。例如,在/project/src/module.js
中require('lodash')
,会先查找/project/src/node_modules
,如果没有找到,再查找/project/node_modules
,以此类推,直到根目录/node_modules
。如果在package.json
文件中有browser
字段指定了某些模块的替代路径,会优先按照该路径查找。 - 加载方式:第三方模块一般是通过npm安装到
node_modules
目录下。加载方式和自定义模块类似,对于JavaScript模块会按.js
等后缀顺序加载执行,对于二进制模块(.node
)也会使用process.dlopen
加载。
- 查找顺序:Node.js会从当前模块所在目录开始,向上级目录查找
require缓存机制
- 工作原理:Node.js使用一个对象来缓存已经加载过的模块。当第一次使用
require
加载某个模块时,Node.js会将该模块的导出对象缓存起来。缓存的键是模块的完整路径(通过path.resolve
解析得到)。之后再次使用require
加载相同路径的模块时,会直接从缓存中返回导出对象,而不会重新加载模块代码。 - 对模块加载的影响:
- 提高效率:避免了重复加载相同模块带来的性能开销,尤其是在大型应用中,多次引用同一模块时,能显著提高加载速度。
- 模块状态一致性:保证了在整个应用中,同一个模块只有一份实例,模块内的状态(例如模块内部的变量)能保持一致性。如果模块中有副作用(例如修改全局变量等操作),由于缓存机制,这些副作用在首次加载模块后就会生效,并且后续再次加载不会重复执行副作用代码,这有助于保持应用状态的稳定。