You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//a.jsconstb=require('./b.js');console.log('在 a 中,b.done = ',b.name);console.log(b);exports.name='a';//b.jsconsta=require('./a.js');console.log('在 b 中,a.done = ',a.name);exports.name='b';
模块的加载规范
对于
js
的模块部分,有好多这方面的文章,所以在这里我就不再赘述了,对于几种模块的加载规范之间的差别,可移步这里简述
module
定义在
node
中,每一个文件都被当成一个独立的模块,而且每个模块都有自己的作用域,这就很好地保证了不同模块之间变量的相互污染。因为每个模块被node
包装成如下所示:exports
属性上的任何方法和属性都可以在外部被调用,模块中的其余变量或属性则不可直接被调用。但是被加载模块中的全局变量可以被外部调用此时可以打印出
test
,但是将a.js
改为如下:此时就会报错。
对于
node
中模块的基础知识,比如文件加载方式这里不展开说,具体可以查看这篇文章exports
、module
和module.exports
module
是当前模块的对象的引用,结构如下:module.exports
是module
的一个属性对象(如上可知),它是由模块系统创建的,并且最终返回给调用的模块exports
是module.exports
的一个引用,相当于一个快捷键:最终返回给
require
的是module.exports
模块。两者之间的相互改变有如下几种情况:
exports
的引用时(即exports
指向了一个新的对象空间),此时不会影响到module.exports
:此时
module
中的exports
对象中只有一个name
属性,而不会有age
属性。因为exports
指向了一个新的空间。exports
的引用时,并且添加一个module.exports
中(并且此时module.exports
有显式声明为一个对象实例)没有的属性时,不会改变module.exports
:此时
module
中的exports
对象中只有name
属性,而没有age
属性,因为原先的module.exports
中没有age
属性,无法添加exports
的引用时,并且添加一个module.exports
中(并且此时module.exports
没有显式声明为一个对象实例)没有的属性时,会改变module.exports
:此时
module
中的exports
对象中既有name
属性又有age
属性exports
的引用,并且添加一个module.exports
中(并且此时module.exports
没有显式声明为一个对象实例)有的属性时,会覆盖module.exports
原有的属性:此时
module
中的exports
对象中既有name
属性,并且值为b;exports
对象上module.exports
进行导出即可exports
导出类,需要使用exports = module.exports = obj
进行hack
即可模块之间的相互引用
之前在做项目的时候,遇到一个场景:
a
模块中引入了b
模块,然后b
模块又引入了a
,然后在b
中访问不到a
的属性,当时还花了好长时间来排查(捂脸)...上面这个场景可以被简单复现为如下:
此时运行
b.js
,会发现打印出的b.name=undefined, b={}
。因为在
b
在require a
的时候,发现a
也require
了b
,为了防止循环引用,a
中此时的b
只是一个exports
未加载完成的副本{},所以没有任何值打印,但是在b
中,可以获取到a.name
属性。所以为了避免出现这种情况,应该尽量避免模块之间的相互引用
主模块
官方文档中对于其实有描述,只是有点简单,之后通过实验了一下,才领会了说的啥。
也就是如果当前要执行当前文件,比如
a.js
,如果此时执行a.js
,那么它的require.main === module
就为true
,但是对于如下场景:此时打印出来的就是false。
因为每个文件模块都会被包装成一个函数,并且会有一个
__filename
的参数,而且__filename === require.main.filename
,所以可以通过检查require.main.filename
来获取当前应用程序的入口点。模块的缓存
模块在第一次被加载之后就被缓存起来了,这意味着以后每一次再调用相同的模块将会返回同样的一个对象。这种处理方式在大多数情况下是很好的。但是如果有一些比较特殊的场景需要删除这个缓存,要怎么做呢
其中
moduleName
是你想要删除缓存的模块名,并且是真实存在的。具体的讨论可以移步这里
总结
以上只是总结一下我对模块这块存在的有误区或者不太了解的地方,涉及的点不太深,只是自己的一点记录,有不对的地方欢迎斧正。
The text was updated successfully, but these errors were encountered: