nodejs中module.exports和exports的区别

最近在学习nodejs,这篇文章就权当是一篇笔记,如果有什么地方有误,望指出。

先说说它们之间的区别:

  • exports只能使用语法来向外暴露内部变量:如exports.xxx = xxx;
  • module.exports既可以通过语法,也可以直接赋值一个对象。

我们要明白一点,exports和module.exports其实是一个东西,不信我们来输出一下

console.log(module.exports === exports);

//输出结果为:true

输出结果是true其实就说明它们就是一个东西,其实exports = module.exports,因为他们是引用类型的一个变量名,所以当exports再指向一个引用类型的时候,那么他们就不再全等。

exports = [0, 1];
console.log(exports === module.exports);

//输出结果为:false

当然,如果直接通过exports.xxx的形式赋值,那么他们依然会指向同一个地址:

exports.array = [0, 1];
console.log(exports === module.exports);

//输出结果为:true

这个时候要明白module.exports和exports的区别,就要清楚什么是值类型,什么是引用类型。我对值类型和引用类型的理解就是,看它是存储在栈上,还是存储在堆上,值类型就是存储在栈上,引用类型是存储在堆上,但是有个很特殊的情况是,引用类型的名字,是存储在栈上,然后这个名字指向了堆上的一个地址,从而可以直接使用变量名,调用堆上的数据。

img

这样可能有点难以理解,我们用代码来简单的认识一下值类型:

let a = 1;
let b = a;
a = 2;
console.log("a的值是:" + a);
console.log("b的值是:" + b);

我们将1赋值为a,然后将a赋值给b,然后我们将2赋值为a那么这个时候a和b分别是多少呢?我们将它运行一下看结果:

img

那么为什么a的值是2,b的值为1呢?是因为当将a赋值给b的时候,相当于是将a的值拷贝给了b也就相当于是重新生成了一个b,那么这个b与a就没有什么关系了,如下图所示:

img

可以看出,这个时候再去改变变量a的值,那么b的值肯定不会发生变化。

那么引用类型呢:

let a = [1, 2];
let b = a;
a[0] = 0;
console.log("a的值是:" + a);
console.log("b的值是:" + b);

那么问题来了,这个时候的b会输出什么结果,是[1,2]还是[0,2]那么我们来进行输出一下

img

这个时候为什么输出的结果是0,2呢?这里就涉及到引用类型了,如下图所示:

img

从图上面看的出来,栈中只是存储了一个变量名字,而数组是存储在堆中。而当将a赋值给b的时候,并不是从堆中拷贝一个数组再让b指向这个数组,而是直接将b指向和a指向的同一个数组。简单来说,可以把变量a看做是银行账户的存折,变量b是银行账户的卡,都是同一个账户,你从存折里面取钱或者存钱,那么卡中的钱也会跟着变多或者变少。

那么我们回到主题,那么我们再来说为什么module.exports可以赋值一个对象,而exports却不可以。要明白这点,就要从nodejs的模块化说起,当nodejs执行模块中的代码时,它会将模块中的代码,用一个函数进行包裹:

function (exports, require, module, __filename, __dirname) {}

这里面的其他参数就在这篇文章中不仔细讲解了,不过可以发现,里面有个熟悉的参数module。这里就要说到exports的本质了,正如上面所说exports = module.exports,也就是说他们指向了堆空间的同一个东西,如果对exports进行赋值,那么exports的指向就不一样了,在另外的文件里面就无法再找到通过exports这个变量传递的东西,而module.exports是在执行模块代码中就将module传入到了函数中,所以即使module.exports的值改变也能够在其他文件中进行调用。

再写到这里的时候我产生了一个疑问,如果将module.exports的指向改变,那么通过exports.xxx传递的值在其他文件中还能进行调用嘛,于是我尝试了下面的代码:

//Test.js
exports.add = 100;
module.exports = 1;
//test1.js文件
let test = require("./Test");
let p = test.add;
let b = test;
console.log("p的值是:" + p);
console.log("b的值是:" + b);
/*
输出结果是:
p的值是:undefined
b的值是:1
*/

可以看出,改变了module.exports的指向后,exports.xxx的值在其他文件中也无法调用。