前言
这篇文章是本人在读红宝书(第四版)4.1.3节-传递参数时引发的思考产出的最终结果,记录一下思考的过程。因为函数传参的过程其实就是变量赋值的过程,所以本文全部用变量赋值举例。若各位大佬有不同见解,欢迎探讨!
先说结论:在传递操作中只有值传递,没有引用传递。
我们大多都是把这两个概念混淆在一起,所以会觉得存在引用传递,接下来我们先区分传递和访问两种概念。
值传递是指在变量赋值的过程,并且会将原变量的值复制一份出来赋给新变量,两个变量互不相干。
ini
const a = 1; // 这是一次值传递操作
let b = a; // 这也是一次值传递操作
b = 2;
console.log(a); // 1 修改b的值不会影响a
访问是指读取变量值的过程,分为按值访问、按引用访问
按值访问
其实值传递的过程中会涉及到访问的操作,比如我有一个变量a值为1,现在要将a的值赋值给一个新的变量b,首先得拿到a的值才能赋值给b,在访问a的具体值的过程所使用的访问方式就是按值访问。
ini
const a = 1;
let b = a; // 这里需要先拿到a的值,才能赋值给b
按引用访问
按引用访问概念和按值访问差不多,我们都知道像对象、数组等等一些复杂的数据类型是存储在堆内存中的。所以我们的变量中存的是存在堆内存中数据的地址,访问该变量时会根据变量对应的内存地址去堆内存中查找对应的数据,这个访问方式称为按引用访问。
ini
const obj = {
a: 1
};
const obj1 = obj; // 这里需要先拿到obj的值,也就是该对象在堆内存中的地址,才能赋值给obj1
可以看到我们在将obj的对象赋值给obj1时,实质上是复制了一份地址给obj1,因此我们可以通过更改变量的地址来使其脱离原来对象的引用。
ini
const obj = {
a: 1,
b: 2,
};
let obj1 = obj;
obj1.a = "a";
console.log(obj); // { a: 'a', b: 2 }
obj1 = {}
obj1.a = "a1"
console.log(obj); // { a: 'a', b: 2 }
console.log(obj1); // { a: 'a1' }
上述代码第一个打印结果证明obj1和obj操作的是同一个对象,因为他们所对应的是同一个堆对象地址,地址相同,所以对象相同。
接下来将obj1赋值为空对象,我们可以先理一下这一步会发生什么。首先有一个空对象,就会在堆内存中开辟一个新的空间来存储这个空对象,然后将这个新空间的地址赋值给obj1。此时obj1和obj对应的对象内存地址已经不一致了,所以接下来对obj1对象的操作不会再影响obj的对象。
所以我们通过给obj1赋值新对象的方式,使其与obj的对象脱离绑定,互不影响,这不就是值传递的概念么。