JS传值OR传引用
outline:
1. call by value(值传递)
2. call by reference(引用传递)
3. call by sharing(共享传递)
这里我们以C语言为例,比较call by value和call by reference1
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
24var 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执行后 |
---|---|---|---|
a | 否 | 1 | 1 |
o1 | 是 | {name: “jiang1”, age: 22} | {name: “jiang1”, age: 23} |
o2 | 是 | {name: “jiang2”, age: 23} | {name: “jiang2”, age: 23} |
从表中,我们可以初步看出几条rules:
- 对于非对象的变量,传递的方式跟值传递是一样的效果,foo中的a并不会影响外面的a的值
- 对于对象类型的变量,传递的方式有点像值传递(比如o2),但是又有点像引用传递(比如o1),我们可以分为两中情况:
- 对于形如o1的,函数接受对象o1,在函数内修改o1的属性,这个时候外面o1会受到影响.
- 对于形如o2的,函数接受对象o2,在函数内修改o2的指向,令o2指向一个新的对象,这种情况外面的o2不受影响.
这种兼具call by value
和call by reference
的传递方式,我们就称为call by sharing
.
对于这种特殊的传递方式,我们可以stackoverflow总结的去理解.没想到跟我们列表总结的3条rules是一样.
下面我们在来看一个实际的例子关于nodejs为什么要有exports和module
这个例子我们只有两个js,分别是test.js和x.js1
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.exports
- 外面的test.js中require得到的是exports.
那么为啥那么这里又要exports又要module.exports呢,感觉多次一举.这里结合我们上面所说的call by sharing,我想大家应该能想到点什么.考虑两个场景,
- 我们要修改exports的属性又要是外面的exports生效(即require可以看到我们给exports绑定的属性)
- 我们要修改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里的传值,我想总结为默认情况下就按照值传递去理解,如果想要实现引用传递,那么就去找这个对象、属性所属的对象就好啦.