We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript里函数参数的传递是按值传递的,而我们知道数据类型里有值类型和引用类型,值类型还好,无论是直接使用,还是作为参数传递,都是直接操作变量的值,也没有什么太难理解的地方。但是对象和数组都是引用类型,就是变量里存放的是数据在内存中的地址,那当引用类型作为参数传递的时候是如何按值传递呢?
在讨论引用类型数据作为参数传递之前,我们先看一下JavaScript中变量是如何存取的。 像String,Number这样的值类型,数据是保存在栈上的,赋值的时候直接在栈上新增一个值的副本
var a = 1; var a2 = a;
此时栈堆里的内容差不多可以理解是这样的
而像Object,Array这样的引用类型,在栈上保存的只是一个内存地址,指向存放在堆内存上的具体值,原因就是内存地址的大小一般是固定的,而实际值基本是大小不定的,存放在栈上会有性能问题。
var a3 = { name: "a3" }
此时的堆栈
每次操作a3的时候,系统会自动根据内存地址去取到实际的值。
复制一个引用类型,只是在栈上新增保存了变量的地址,是一个对引用地址的拷贝,并不会去拷贝堆上的值。
var a4 = a3;
此时我们再新增一个引用变量
var a5 = { age: 1 }
改变这几个对象的值后,你会发现,只改变了栈上保存的内存地址,实际上在堆上的内容并没有改变
a4 = a5; a5 = a3;
如果要改变引用类型的实际值,会通过栈上的地址去访问堆上的值,此时不会改变栈上的地址,而是改变堆上的值了。
a4.age = 2
也就是说,对于引用类型变量,以变量名为操作的,比如var o = {...},任何o = xxx,xxx是值类型也好是引用类型也好,都只会修改o在栈上的内容,即修改o的引用地址。 而对变量值的操作,比如o.xxx = yyy;也不论yyy是什么类型,都是通过o在栈上的地址,去访问到堆上的实际值,然后修改。
由上面变量的规则我们来探讨一下函数的参数是如何按值传递的。 首先是值类型的参数,这种很容易理解,函数内部拿到实参值的一个拷贝,在函数体里做任何处理都不会影响到原变量值。 然后是引用类型的参数,传递的是该变量存放在堆上的值吗? 因为函数参数实际也是变量,这个变量是不可能以值类型的方式存放引用类型的值的,所以函数参数接受的并不是引用类型参数的引用地址,也不是参数的实际值,而是引用地址值的一个拷贝。
函数在接收到一个引用类型的参数时,在栈上新增一个引用地址的拷贝,在之后通过这个地址去访问实际的值。
在这种情况下,如果是修改参数的值,完全可以通过参数的引用地址去修改值。并且会反应到原变量上。
function reName(o){ o.name = "new name" } reName(a3) a3 // {name: "new name"}
但是如果要通过引用地址去改变变量本身的话,是不可以的,因为变量的引用地址此时还是存放在栈上,在函数体里改动的只是参数args里的一个拷贝,而参数会在函数调用结束后被释放掉,相当于做了一遍无用操作。
function reName2(o){ o = 1; } reName2(a3) a3 // {name: "new name"}
对的,a3并没有改变,改动的只是栈上args里保存的值,将一个内存地址改成了一个值类型,但是函数调用完之后被释放了, 这个操作完全是无效的。
所以函数参数是引用类型的时候,是有一个坑的,具体表现就是只能通过传递引用类型来改变它的值,不能改变它的指向。
function func(o){ ... o = xxx; // 这样的操作是无效的,因为它改的实际是args,并不会反映到实参上 ... }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
JavaScript里函数参数的传递是按值传递的,而我们知道数据类型里有值类型和引用类型,值类型还好,无论是直接使用,还是作为参数传递,都是直接操作变量的值,也没有什么太难理解的地方。但是对象和数组都是引用类型,就是变量里存放的是数据在内存中的地址,那当引用类型作为参数传递的时候是如何按值传递呢?
变量的存放方式
在讨论引用类型数据作为参数传递之前,我们先看一下JavaScript中变量是如何存取的。
像String,Number这样的值类型,数据是保存在栈上的,赋值的时候直接在栈上新增一个值的副本
此时栈堆里的内容差不多可以理解是这样的
而像Object,Array这样的引用类型,在栈上保存的只是一个内存地址,指向存放在堆内存上的具体值,原因就是内存地址的大小一般是固定的,而实际值基本是大小不定的,存放在栈上会有性能问题。
此时的堆栈
每次操作a3的时候,系统会自动根据内存地址去取到实际的值。
复制一个引用类型,只是在栈上新增保存了变量的地址,是一个对引用地址的拷贝,并不会去拷贝堆上的值。
此时的堆栈
此时我们再新增一个引用变量
此时的堆栈
改变这几个对象的值后,你会发现,只改变了栈上保存的内存地址,实际上在堆上的内容并没有改变
如果要改变引用类型的实际值,会通过栈上的地址去访问堆上的值,此时不会改变栈上的地址,而是改变堆上的值了。
此时的堆栈
也就是说,对于引用类型变量,以变量名为操作的,比如var o = {...},任何o = xxx,xxx是值类型也好是引用类型也好,都只会修改o在栈上的内容,即修改o的引用地址。
而对变量值的操作,比如o.xxx = yyy;也不论yyy是什么类型,都是通过o在栈上的地址,去访问到堆上的实际值,然后修改。
函数参数传递
由上面变量的规则我们来探讨一下函数的参数是如何按值传递的。
首先是值类型的参数,这种很容易理解,函数内部拿到实参值的一个拷贝,在函数体里做任何处理都不会影响到原变量值。
然后是引用类型的参数,传递的是该变量存放在堆上的值吗?
因为函数参数实际也是变量,这个变量是不可能以值类型的方式存放引用类型的值的,所以函数参数接受的并不是引用类型参数的引用地址,也不是参数的实际值,而是引用地址值的一个拷贝。
函数在接收到一个引用类型的参数时,在栈上新增一个引用地址的拷贝,在之后通过这个地址去访问实际的值。
在这种情况下,如果是修改参数的值,完全可以通过参数的引用地址去修改值。并且会反应到原变量上。
但是如果要通过引用地址去改变变量本身的话,是不可以的,因为变量的引用地址此时还是存放在栈上,在函数体里改动的只是参数args里的一个拷贝,而参数会在函数调用结束后被释放掉,相当于做了一遍无用操作。
对的,a3并没有改变,改动的只是栈上args里保存的值,将一个内存地址改成了一个值类型,但是函数调用完之后被释放了,
这个操作完全是无效的。
所以函数参数是引用类型的时候,是有一个坑的,具体表现就是只能通过传递引用类型来改变它的值,不能改变它的指向。
The text was updated successfully, but these errors were encountered: