# CommonJS中的导出
// cal.js中导出一个对象 module.exports = { name: 'cal', add: function(a, b) { return a + b; } } // 为了书写方便,可以像下面简写 exports.name = 'cal'; exports.add = function(a, b) { return a + b; }
上面两段代码,在实现效果上没有任何的不同.
其内在机制是将exports指向了module.exports,而moudle.exports在初始化时是一个空对象.我们可以简单的理解为,CommonJS在每个模块的首部默认添加了以下代码:
var module = { exports: {} } var exports = module.exports
因此,为exports.add赋值,相当于在module.exports对象上添加了一个属性
# 注意:
在使用exports时要注意一个问题,即不要直接给exports赋值,否则导致其失效,如:
exports = { add: function(a, b) { return a + b; } }
上面代码中,由于对exports进行了赋值操作,使其指向了一个新的对象,module.exports却仍然是原来的空对象,因此add属性并不会被导出,例如下面这个代码:
var module = { exports: {} } var exports = module.exports console.log(exports === module.exports) // true exports = { add: function(a, b) { return a + b } } console.log(exports === module.exports) // false 注意这个地方是false
# 注意:
另一个需要注意的问题,module.exports和exports不要混用
exports.add = function(a, b) { return a + b } module.exports = { name: 'cal' }
上面代码中的module.exports指向了另一个对象,导致之前的add属性被丢失,所以最后导出只有name属性
# 注意:
在module.exports和exports后面的代码依旧会执行,但不建议这样写
# CommonJS中的导入
使用require导入模块时,有下面两种情况:
1.require的模块是第一次被加载.这时会首先执行该模块,然后导出内容
2.require的模块曾被加载过.这时该模块的代码不会再次执行,而是直接导出上次执行后的结果
例如:
// cal.js console.log('cal.js') module.exports = { name: '我是乔峰', add: function(a, b) { return a + b } } // index.js // 第一次require const add = require('./cal').add console.log(add(1, 2)) // 第二次require const name = require('./cal').name console.log(name)
此时的结果如下:
从结果上可以看出,尽管我们require两次,但其内部代码只会执行一遍.
其原理大致如下:
在模块中,有一个module对象用来存储信息,这个对象中有一个属性loaded用于记录该模块是否被加载过.它的默认值是false,当模块第一次被加载和执行过后会变成true
如果后面再次加载时,检测到loaded为true,则不会次再执行模块代码
# 注意:
有时我们加载一个模块,不需要获取其导出的内容,只是想要通过执行它而产生某种作用,比如把它的接口挂载到全局对象上,此时直接使用require即可
require('./task.js')
# 注意:
另外,require可以接收表达式,可以动态加载指定的模块
const moduleNames = ['foo.js', 'bar.js'] moduleNames.forEach(name => { require('./' + name) })