JavaScript 之 函数参数的传递
按值传递
继上篇说的,JavaScript的基本数据类型有7种:null、undefined、string、number、boolean、symbol、bigint,而它们的传递方式则是按值传递(既不会对原数值造成影响),下面看一个例子:
var x = 1;
console.log('x: ', x); // 1
function changeX(val) {
val = 2;
console.log('val: ', val); // 2
}
changeX(x);
console.log('x: ', x); // 1
由上面的例子可以看出,在函数传参的过程中,我们只是传递了数值,因此当在函数中对参数进行了改变,也不会对原来的x造成影响。
但是如果传递的是对象呢?
在《JavaScript高级程序设计》第三版中,是这样描述的:
ECMAScript中所有函数的参数都是按值传递的。
网上看了很多文章,个人感觉还不是理解的很透彻,以下是个人的一些看法:
在函数传参的过程中,假设我们传递的不是基本数据类型而是对象,那么我们传递的其实是对象地址的副本。下面看一个例子:
var x = {
value: 1
}
console.log('x: ', x); // {value: 1}
var y = {
value: 2
}
console.log('y: ', y); // {value: 2}
function changeXY(valX, valY) {
console.log('before valX: ', valX); // {value: 1}
console.log('before valY: ', valY); // {value: 2}
valX.value = 'changeX';
valY = 'changeY';
console.log('valX: ', valX); // {value: 'changeX'}
console.log('valY: ', valY); // 'changeY'
}
changeXY(x, y);
console.log('x: ', x); // {value: 'changeX'}
console.log('y: ', y); // {value: 2}
在这里,x、y都是一个对象,因此,当调用函数changeXY时,我们传递的都是对象地址的复制。而在函数内部,分别进行了两种不一样的操作,一个是对对象数值的更新,另一个则是对对象进行赋值。
我认为这里理解的关键是:
当进行赋值操作后,就等于重新开辟了一个空间,因此valY相当于重新指向了一块内存空间,但原来valY仍然是是指向原来的内存地址,因此并不会对y造成影响。
而如果是值的操作,因为参数传递的是地址的副本,所以其实两者指向了同一块内存空间,因此当valX发生了变化时,x也会跟着发生了改变。
以下图片是对其栈内存和堆内存进行分析:
关于引用类型
在JavaScript中,引用类型其实指的就是对象类型(对象、函数、数组),在使用它们的时候,它们都是通过地址指向堆内存的一块空间。
以上是对引用类型的一些见解,如有错误或不足,希望大家可以多多包涵和指出!
参考资料:
-
JavaScript深入之参数按值传递
https://github.com/mqyqingfeng/Blog/issues/10
(里面有作者的理解和讨论区各个大神的讲解,大致看完之后感觉收获不少,可以从多个角度进行理解,个人认为本质上还是对堆内存和栈堆内存的理解)
-
Primitive Types & Reference Types in JavaScript —— Bran van der Meer
https://docstore.mik.ua/orelly/webprog/jscript/ch04_04.htm
(英文文章,耐心读下来其实发现还是从堆栈这块进行分析)