关于CommonJS规范中module.exports与exports的区别
之前在面试的时候曾经被问到过关于commonJS规范中module.exports与exports的区别。之前仅仅知道exports是module.exports的一个引用而已,其之间的具体区别没有过多关注。最近整理了一下两者的区别,究竟什么时候两者是相同的,什么时候两者又是不同的。
CommonJS规范中模块的写法以及引用
commonJS规范中定义了模块的概念,其中包含核心模块以及文件模块。为什么需要module.export或者exports的原因在于暴露给外部模块引用本模块时的函数或变量。在外部模块中通过require接口引用其他模块。在被引用模块中通过module.exports或者exports暴露该模块的函数或者变量。示例如下:
1 | sum.js |
上述示例演示了一个最基本的模块的写法以及引用。
两者之间的联系与区别
module.exports在模块开始的时候是一个空对象{},exports为module.exports的一个辅助工具,其指向module.exports指向的对象。即类似于:
1 | module.exports = {}; |
这就说明了module.exports与exports指向同一块内存地址。require接口调用的时候以module.exports的值为准。
两者在以下情况下是一致的:
1 | exports.name = name; |
两者在以下情况下是有区别的:
1 | exports = { |
总结:两者在以对象的属性导出的时候是一致的,此时用exports导出和module.exports是一样的效果。如果只使用exports尝试导出一个对象或者function的时候此时会导致不一致的情况。
使用exports导出与module.exports导出出现不一致的原因
之所以用exports与module.exports出现不一致的原因在于:两者开始引用同一块内存地址,如果单纯以对象的属性的方式导出的话(情况一),此时相当于给两者共有的对象添加一个属性并导出。一旦只是用exports导出的是一个function或者一个对象,则exports指向的对象发生了变化,与module.exports
变失去了联系。require接口是以module.exports对象返回的值为依据,因此此时如果仅适用exports导出一个函数或者对象的话会出现错误。下面用一个最简单的示例解释两个变量同时引用一个对象的情况。示例如下:
1 | var person1 = { |
上例中情况1说明person2与person1指向的是同一个对象,通过peroson2.gender的方式对两者共同引用的对象添加了一个gender属性。这种情况下打印person1.gender也得到male。情况2中person2重新引用了另外一个新对象,此时person1与person2引用两个不同的对象,两者无联系。这也就解释了使用exports一个对象的时候会出现module.exports不一致的情况。
总结
这个知识点的重点在于对JavaScript中变量引用的对象的理解。JavaScript中对象名仅仅是一个引用。通过对象名之间的赋值,本质上是使两者指向同一块内存上的地址。同样的现象在JavaScript中讲述继承重写原型prototype的时候也曾出现。比如:如果使用Child.prototype.getName=function(){}方式为Child的prototype添加一个方法。这种方式不会重写函数的原型,仅仅是在函数原型的基础上添加一个属性而已。如果使用这种方式:
1 | Child.prototype={ |
这种方式相当于重写了prototype原型。prototype原型重新指向了另外一个新的对象,一般需要在这种情况下的继承需要显示的指明constructor属性以达到标明对象类型的目的。
关于CommonJS规范中module.exports与exports的区别
https://monster1935.cn/blog/2016/07/19/关于commonJS规范中module.exports与exports的区别/