圣人不死,大盗不止。 --庄子·外篇·胠箧第十
在JavaScript中,参数的数据传递方式一般是按值传递、引用传递,今天遇到有人问如下这个问题
ini
const obj = {
value: 1
};
function foo(o) {
o = 2;
console.log(o); //2
}
foo(obj);
console.log(obj.value); // 1
console.log(obj); // { value: 1 }
问为什么obj的值没有改变,按照引用传递的方式,最后一行打印的值不是2,而是{ value: 1 }
,
一开始给我也问懵了。确实,最近写iOS代码比较多,按照OC语言的逻辑,如果原先存在一个引用型数据类型A,给这个A赋值引用数据类型B,那么就会A等于B,A和B的内存地址是同一块,原先的A就不存在了。如果按照这个逻辑,确实应该打印2,但是!这是JS,不是OC,反应了片刻,和他简单介绍了JS参数传递的几种方式:
按值传递
这个是最基本的传递方式,最符合直观认知的。大家看一下代码就会明白,即使不会JS会、没啥编程经验的人也能理解。
scss
function add(param){
param+=10;
console.log(param);//11
}
const num = 1;
add(num);
console.log(num);//1
这里,参数param的改变对于num是没有任何影响的,因为这里是num的值复制给了函数add内部的局部变量param,所以在函数内部改变param的值并不会影响外部num的值。
引用类型
红宝书中这么介绍引用类型的:
ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数 中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是 引用值,那么就跟引用值变量的复制一样。
话不多说,上代码:
kotlin
const value = {
value: 1
};
function fun(o) {
o.value = 2;
console.log(o.value);//2
console.log(o);//{value: 2}
}
fun(value);
console.log(value);//{value: 2}
console.log(value.value);//2
函数内部对形参的操作影响到了外部实参,这里就是引用类型。hba
共享类型
那什么是共享类型呢?
说实话我暂时没在红宝书中找到这种说话,但是'坊间'一直流传着共享类型的传说。
还是刚刚那段代码:
ini
const obj = {
value: 1
};
function foo(o) {
o = 2;
console.log(o); //2
}
foo(obj);
console.log(obj.value); // 1
console.log(obj); // { value: 1 }
简单来说就是:对于传递到函数参数的对象类型,如果直接改变了拷贝的引用的指向地址,那是不会影响到原来的那个对象;如果是通过拷贝的引用,去进行内部的值的操作,那么就会改变到原来的对象的。
意味着,这段代码的执行关键就在 o = 2;
将原先存放参数o的内存地址,存放了2(这里可能对于地址更换的细节有出入,但是不影响理解),也就是原先的参数o和obj没有任何关系了,转而存放了2这个值,赋值操作导致标识符发生了重新绑定Rebinding,重新复制的标识符会指向新的内存空间。