前言
之前写博客,经常需要引用一些基础的内容,每次都花不少时间找合适的文章,索性花点时间自己写,也当是巩固下基础。于是有了这个系列(JS核心基础)的文章。
目前已经完成的文章:
从promise到await - 掘金 (juejin.cn)
概论
在《JavaScript高级程序设计》中,参数传递一节直接了当地说:
ECMAScript中所有函数的参数都是按值传递的。
那么参数传递都有哪些形式,js的参数传递又有怎样的特殊之处?
本文将从内存的角度讲解这个问题。
一,值传递和引用传递的区别
1.1,值传递
值传递就是把函数外部的值复制给函数内部的参数,函数中对该值的修改不会影响原有值。
var value = 1;
function change(val) {
val = 2;
console.log(val); //2
}
change(value);
console.log(value) // 1
执行change时对应的内存图如下:
当执行change的时候,是把value=1的值作为实参传递进去,这样在change的执行上下文中的val=1,当执行到val = 2;的时候,change执行上下文中的val变量变成2。
可以看到全局上下文的value还是等于1,并没有被修改。这就是值传递。
1.2,引用传递
引用传递就是传递对象的引用,函数内部对参数的任何改变都会影响该对象的值,因为两者引用的是同一个对象。
var obj = {
value: 1
};
function foo(o) {
o.value = 2;
console.log(o); //{ value: 2 }
}
foo(obj);
console.log(obj) // { value: 2 }
执行到foo函数中的时候,内存的状态如下图:
看起来js传递引用类型的实参的时候,就是利用的引用传递。但是红宝书又明确说是按值传递。那问题出在那里了?
1.3,共享传递
引用传递的话,从理论上讲,如果函数中修改了该入参,原对象必被变更。因为修改的是引用值指向的堆内容,而不是修改引用地址(上图的1002),那么看如下代码:
var obj = {
value: 1
};
function change(o) {
o = 2;
console.log(o); //2
}
change(obj);
console.log(obj) // { value: 1 }
对应的内存图解:
可以看到。函数执行后,并没有修改外层的对象值,而是把引用地址修改了(o=2)。
其实这种传递叫共享传递:在传递对象的时候,传递对象的引用的副本。
也就是说: 按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!
换句话说就是change上下文中的o和全局上下文的obj的值,是不相关的。o要改成啥是它自己的,只是最开始传入的时候,它是个引用值,能够访问和修改外层的obj罢了。
二,总结
所以说,红宝书中说的其实也没错:
ECMAScript中所有函数的参数都是按值传递的。
对于普通类型的数据,它将值复制一份。
对于引用类型的数据,他将引用值复制一份。
本质上,就是值的复制。