outline:

1. call by value(值传递)

2. call by reference(引用传递)

3. call by sharing(共享传递)

这里我们以C语言为例,比较call by value和call by reference

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int test(int a1,int *b1){
a1 = 3;
*b1 = 4;
}
int main(){
int a = 1;
int b = 2;
printf("a = %d, b = %d\n",a, b);//a=1,b=2;
test(a, &b);
printf("a = %d, b = %d\n",a, b);//a=1,b=4;
return 0;
}

在上面这段C代码中,
a => a1,按照值传递(call by value)的方式,修改a1并不影响a
b => b1,按照引用传递(call by reference)的方式,修改b1影响b

那么JS中到底是按照什么方式传值呢?
先看一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var a = 1;
var o1 = {
name : 'jiang1',
age : 22
};
var o2 = {
name : 'jiang2',
age : 23
};
function foo(a,o1,o2){
a = 2;
o1.age = 23;
o2 = {
name: 'jiang3',
age:24
};
}
console.log(a);//1
console.log(o1);//{name: "jiang1", age: 22}
console.log(o2);//{name: "jiang2", age: 23}
foo(a,o1,o2);
console.log(a);//1
console.log(o1);//{name: "jiang1", age: 23}
console.log(o2);//{name: "jiang2", age: 23}

这里我们把a,o1,o2的foo函数执行前后变化情况列出来,如表所示:

变量是否是对象foo执行前foo执行后
a11
o1{name: “jiang1”, age: 22}{name: “jiang1”, age: 23}
o2{name: “jiang2”, age: 23}{name: “jiang2”, age: 23}

从表中,我们可以初步看出几条rules:

  1. 对于非对象的变量,传递的方式跟值传递是一样的效果,foo中的a并不会影响外面的a的值
  2. 对于对象类型的变量,传递的方式有点像值传递(比如o2),但是又有点像引用传递(比如o1),我们可以分为两中情况:
    • 对于形如o1的,函数接受对象o1,在函数内修改o1的属性,这个时候外面o1会受到影响.
    • 对于形如o2的,函数接受对象o2,在函数内修改o2的指向,令o2指向一个新的对象,这种情况外面的o2不受影响.
      这种兼具call by valuecall by reference的传递方式,我们就称为call by sharing.
      对于这种特殊的传递方式,我们可以stackoverflow总结的去理解.没想到跟我们列表总结的3条rules是一样.

下面我们在来看一个实际的例子关于nodejs为什么要有exports和module
这个例子我们只有两个js,分别是test.js和x.js

1
2
3
//test.js
var x = require('./x.js');
console.dir(x)

首先我们通过一个简单的log看下exports和module的关系:

1
2
3
//x.js
console.log(exports===module.exports)
console.dir(module);

得到如下结果
exports_module.png
我们可以肯定

  1. exports就是module.exports
  2. 外面的test.js中require得到的是exports.

那么为啥那么这里又要exports又要module.exports呢,感觉多次一举.这里结合我们上面所说的call by sharing,我想大家应该能想到点什么.考虑两个场景,

  1. 我们要修改exports的属性又要是外面的exports生效(即require可以看到我们给exports绑定的属性)
  2. 我们要修改exports同时又要外面的exports生效(即require可以看到我们给exports赋予的新的值)
    对于第一种情况,大家的代码通常都能实现,比如
    1
    2
    3
    4
    5
    //x.js
    var test = function(){
    console.log("test");
    }
    exports.test = test;

这个时候我们结合call by sharing想一想,是不是我们改变了exports的属性就可以改变外面的exports.
那么对于第二种情况,我们有该怎么办呢?也许有的说我可以直接复制

1
2
3
4
5
//x.js
var test = function(){
console.log("test");
}
exports = test;

很可惜,并没什么卵用.这种用法和我们上面的o2不是一样的情况么?其实估计聪明的读者有的可能已经想到了,exports是moudle的属性,既然我改exports没有用,那么我来改module.exports不久可以了么.bingo!就是这样的

1
2
3
4
5
//x.js
var test = function(){
console.log("test");
}
module.exports = test;

到这里,我想读者们应该明白了为什么要有exports和module了吧.
对于js里的传值,我想总结为默认情况下就按照值传递去理解,如果想要实现引用传递,那么就去找这个对象、属性所属的对象就好啦.