「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。
在上篇文章中,我们介绍了为什么要使用模块化JavaScript模块化总结(一)。
我们知道,模块化有很多规范,包括AMD,CMD,CommonJs,以及ES6新增的模块化等等。如今,常用的是CommonJS和ES6新增的模块化。本篇文章主要介绍commonJS规范。
CommonJS规范和node关系
CommonJS是一种规范,它在服务器端比较有代表性的实现是node。node中使用的js是符合CommonJS规范的。可以说,node是CommonJS规范的一种实现。
CommonJS最初提出来是在浏览器以外的地方使用的,并且那时候被命名为ServerJS。后来它被使用在其他地方,为了体现它的广泛性,修改为CommonJS。
我们知道,node中是支持CommonJS的,可以让我们很方便地进行模块化开发。在node中,每一个js文件都是一个模块。我们可以使用exports、module.exports、require进行模块化开发。
模块化的核心是导入和导出。node中对其进行了实现:exports和module.exports可以对模块中的内容进行导出。require函数可以导入其他模块(自定义模块、系统模块、第三方库模块)中的内容。
CommonJS的基本使用
我们在haha.js文件中定义了一些变量。
const name = 'haha'
const age = 18
function sum(num1, num2) {
return num1 + num2
}
然后,想要在main.js其他文件中使用这些变量,我们可以直接使用吗?
console.log(name);
我们可以看到报错了。这是因为每个js文件是一个模块,每个模块有自己的独立空间。其他文件是访问不到我自己的独立空间的。
那么要使用其他模块的变量该怎么办呢?我们可以通过CommonJS规范使用module.exports对内容进行导出,在其他文件中使用require对其他文件导出的内容进行导入。
module.exports是个对象。module是模块本身的对象,exports也是个对象。
在haha.js中,我们可以给module.exports添加属性,该属性为要导出的内容。也可以给module.exports重新赋值为一个新的对象,对象中包含着要导出的内容。
// module.exports.name = name
module.exports = {
name,
age,
sum
}
在main.js中,通过require(path)来接收haha.js中导出的对象。
const haha = require('./haha')
console.log(haha.name);
console.log(haha.age);
我们也可以使用对象的解构。
const {name, age} = require('./haha')
console.log(name);
console.log(age);
需要注意的是,module.exports导出的对象和require接收的对象是同一个,在一个文件中进行修改,那么另一个文件中的内容也会相应跟着修改。
// haha.js文件
const info = {
name: 'haha',
age: 18,
foo: function () {
console.log('foo函数');
}
}
setTimeout(() => {
// info.name = 'xixi'
console.log(info.name);
}, 2000)
module.exports = info
// main.js文件
const haha = require('./haha')
setTimeout(() => {
// console.log(haha.name);
haha.name = 'xixi'
},1000)
我们可以看到在main.js中修改导入的对象的属性,在haha.js中导出的对象的属性也被修改了。
exports导出
每个模块都有exports对象,这个exports默认指向的是空对象,可以在这个对象中添加需要导出的属性。
bar和exports是同一个对象,bar对象是exports对象的浅拷贝。浅拷贝的本质是引用赋值。exports对象中的修改会导致main中bar对象也会被修改。
// haha.js文件
const name = 'haha'
const age = 18
function sum(num1, num2) {
return num1 + num2
}
exports.name = name
exports.age = age
exports.sum = sum
// main.js文件
const haha = require('./haha')
console.log(haha.name);
console.log(haha.age);
console.log(haha.sum(20, 30));
在node中,我们还可以通过module.exports进行导出。它们之间有什么关系呢?
- commonjs中是没有module.exports的概念的
- 但是为了实现模块的导出,node使用的是Moodule的类,每一个模块都是Module的一个实例,也就是module。 new Module() => 一个文件就是一个module实例。这个module实例就是js文件另外的一个特殊全局对象module。在所有文件里面都有一个module对象。
- 所以在node中真正用于导出根本不是exports,而是module.exports
- 因为module才是导出的真正实现者
- 在三者引用的情况下,修改了exports中的name属性,module.exports和main中的bar对象的name属性也会改变
- 在三者引用的情况下,修改main中bar的name属性,另外两个地方也会改变
- 如果module.exports不再引用exports对象, 那么修改exports没有意义。 需要了解的是,node中真正导出的是module.exports,而不是exports。那为什么exports也能导出呢?
那是因为module对象的exports属性是exports对象的一个引用。在例子中,也就是module.exports = exports = main中的haha。