基于CommonJS中的导入和导出

# 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)

此时的结果如下:

基于CommonJS中的导入和导出

从结果上可以看出,尽管我们require两次,但其内部代码只会执行一遍.

其原理大致如下:

在模块中,有一个module对象用来存储信息,这个对象中有一个属性loaded用于记录该模块是否被加载过.它的默认值是false,当模块第一次被加载和执行过后会变成true

如果后面再次加载时,检测到loaded为true,则不会次再执行模块代码

# 注意:

有时我们加载一个模块,不需要获取其导出的内容,只是想要通过执行它而产生某种作用,比如把它的接口挂载到全局对象上,此时直接使用require即可

require('./task.js')

# 注意:

另外,require可以接收表达式,可以动态加载指定的模块

const moduleNames = ['foo.js', 'bar.js']
moduleNames.forEach(name => {
  require('./' + name)
})


上一篇:vue-cli lib模式打包umd分析


下一篇:计算机图形学入门(八)-着色(插值、高级纹理映射)