函数的值传递和引用传递
先写一个简单的小函数。
例 1
// JavaScript
function test(a){
console.log(a); // a = 3;
a = 14;
console.log(a); // a = 14;
}
var s = 3;
test(s);
console.log(s); // s = 3;
值传递
所谓值传递就是,把函数外部的变量“值”复制给函数内部的参数,就和把值从一个变量赋值给另一个变量一样。
例如:
let x = 5;
let y = x;
// ......其他操作
此时x把值“5”传递给了y,x和y都等于5,在后面的其它操作里面无论修改x还是y,它们互相之间都是不会影响的,比如把x改为6,y依然是5,反之亦然。
在 例1 的函数中,在函数内部对变量a进行任何操作,都不会影响到函数外部的变量s。调用函数的时候,变量s只是把自己的值传递给了函数内部的变量a。所以称为值传递。
这里所说的“值”均指的是,变量在栈内存中保存的数据。而不是堆内存,记住这点很重要。
引用传递
引用传递,是指直接将变量s 的内存地址(栈内存地址)传递给函数test中的内部变量a,外部的变量s和函数内部的变量a指向了同一个内存地址(栈内存地址),此时在函数test内部对这个地址中存储的数据进行了修改,函数外部的s,同样会受到影响。
当然了,JavaScript不支持引用传递。支持引用传递的语言有比如 C++
假设: 如果javascript 支持引用传递的话,程序的输出将会变成
例 2
// javascript
function test(a){
console.log(a); // a = 3;
a = 14;
console.log(a); // a = 14;
}
var s = 3;
test(s);
console.log(s); // s = 14; <--- 这里变了
vue官方文档中的这个动图挺形象的。
变量的按值访问和引用访问
看完了上面的,看下面的例子
例 3
// javascript
function test(a){
console.log(a); // {d:12};
a.d = 14;
console.log(a); // {d:14};
}
var s = {
d:12,
};
test(s);
console.log(s); // {d:14};
看完它的输出结果,不由得想,不是说JavaScript不支持引用传递的么?
为什么函数内部对变量的操作还是影响到了函数外面的变量。这就要涉及到变量的两种访问方式了。按值访问和按引用访问。
按值访问
按值访问的变量,一般都是基本数据类型,他们占用的内存空间大小固定,所以这类变量的值都直接存储在栈内存中,当我们访问这类变量的时候,直接返回的就是在栈内存中存储的数据。
按引用访问
按引用访问一般都是对象。这类变量由于占用的内存大小不固定,甚至可以随时增加新的属性导致占用内存增大,所以他们存储在堆内存中,把它在 堆内存中的地址 存储到 栈内存 中。当我们访问这种变量的时候,首先在取出在 栈内存 中存储的值即 堆内存地址,再根据堆内存的地址,去堆内存中拿到变量真正的值。
再说一遍 :函数的“值传递”传递的是变量在 栈内存中存储的数据,对于按引用访问的变量,上面的例3中,变量s将它在栈内存中存储的数据也就是 一个对象的堆内存地址,传递给了函数内部的变量a,在函数内部对变量a操作的时候,操作的是堆内存中的数据。而外部的变量s保存的堆内存地址和函数内部的变量a 保存的堆内存地址是同一块,所以外部的变量s的值就受到了影响。
关于栈内存和堆内存
栈内存
栈内存是一个先进后出的数据结构。有序存储,读写效率高,它由操作系统分配和释放。一旦出栈,就被释放。它一般不是很大,基本上在几MB的级别,跟操作系统以及我们的设置有关。
堆内存
堆内存是由我们的程序主动向操作系统申请的,所以使用后我们要主动释放内存。如果不释放无用的堆内存,就造成了内存泄露。泄露的内存部分,我们程序已不再使用可是我们没有主动告诉操作系统,操作系统也无法把它分配给其他的程序使用。一直到我们的程序终止,操作系统回收全部内存。像C语言什么的内存管理部分就是一座难以翻越大山,不过现在的大部分语言比如JavaScript、Java ,基本上都自带垃圾回收机制,检测到无人使用的堆内存就及时释放给操作系统,所以也就不需要程序员过多的参与了。 当然了再厉害的程序也架不住人的骚操作,写程序的时候还是应该要注意点。
堆内存有多大?
理论上电脑内存(含虚拟内存)有多大,程序就能申请多少,当然了这只是理论上。全都给你的话操作系统自己都无处容身了,(*^▽^*)。
总结一下:把栈内存比作一个记录人的信息册子的话,我们的程序只去栈内存取得人的信息,有的人(基础类型)信息很简单三两行就搞定,于是就直接记录在了这个小册子上。有的人(复合类型,对象什么的)信息丰富足够写一本书了,这个小册子写不下,就它的信息写成一本书,放到图书馆(堆内存)里,然后在小册子上记录下这个人的信息存储在图书馆的几层第几排书架的第几层上,即可。然后我们的程序访问的时候先去小册子上查询,如果记录的是对应的数据就直接返回这个数据,如果发现上面记录的不是具体信息而是一个地址,那就根据查到具体地址然后再去图书馆(堆内存)里面找到这个人的信息,再返回。