剖析 exports=module.exports=xxx 的写法
曾经好久好久被这两个东东搞的晕头转向,今天无意间看到了 cNode 社区一篇博客,给我了灵感,算是把这个问题彻底想通了。在作者原文的基础上加上图文解说,希望能把这个问题解释的更透彻点,对那些同样有疑惑的朋友们有些许启发。
1. js 中对象的复制是指针的复制
这句话最早在 《javascript 高级程序设计》中读到过,当时也明白具体咋回事,没想到挪到实际应用还是不能化为己用。
先看个例子:
var a = {'name': 1};
var b = a;
console.log(a);
console.log(b);
b.name = 2;
console.log(a);
console.log(b);
b = {'name': 3}
console.log(a);
console.log(b);
打印出来的结果是这样的:
{name: 1} // a
{name: 1} // b
{name: 2} // a
{name: 2} // b
{name: 2} // a
{name: 3} // b
下面来解释下为什么:
第一次操作 var b = a; 将 a 的值复制给 b,由于 a 是个对象,所以复制的是指向对象的指针,之后 b 才指向了同一个对象。
第二次操作通过 b 变量对对象进行了修改,由于 a 和 b 还是指向同一个对象,所以仍然斗都打印出 2。
第三次操作在变量值空间创建了一个新的对象,将赋值给 b,与将 a 的值复制给 b一样,此时 b 的指针指向了新的对象。
通过以上说明,可以得出结论:对象的复制是指针的复制。
2. 非对象的复制是值的复制
var a = 1;
var b = a;
console.log(a);
console.log(b);
b = 2;
console.log(a);
console.log(b);
打印结果为:
1 // a
1 // b
1 // a
2 // b
结论: 改变 b 的值并不会对 a 有任何影响,非对象值的复制就是值的复制。
3. 解释 exports=module.exxports=xxx的写法
node 中规定:
-
module.exports 初始值为一个空对象 {};
-
exports 是指向的 module.exports 的引用;
-
require() 返回的是 module.exports 而不是 exports。
依然用图文的方式解释:
-
起初,module.exports 指向一个空对象,而 exports 指向 module.exports 的引用,也指向了这个空对象。
-
对 exports 对象添加 age 属性,并赋值 20,这里是为 exports 对象添加一个属性而不是对exports 对象进行赋值,所以引用依然没有改变
-
对 exports 对象添加 say 属性,值为一个方法。
这里主要想说明白两个问题:
-
exports.say = function(){}
为 exports 对象添加了一个属性,值为一个方法,不是一个复制操作,不会改变 exports 的引用
-
exports = function say () {}
say 方法是个对象,将这个对象复制给左边的 exports 对象,此时会改变 exports 的引用
-
-
此时修改 module.exports 指向一个新的对象,改变了指针,导致 exports 不再指向 module.exports 的引用
考虑到 require() 接收的是 module.exports 对象,而不是 exports 对象,这时候对 exports 做的任何操作都不会起作用了。
-
通过 exports = module.exports 将 exports 的指针再次指向 module.exports
4. 结论
-
非对象数据类型有字符穿(String)、数值(Number)、布尔值(Boolean)、Null、Undefined,这些类型对应的值的复制就是值的复制;
-
对象(对象字面量 {}、数组[]、函数 Function等)的复制为引用(也叫指针)的复制;