值传递应该是JS最基本的一个知识点了,网上也有很多说法,有按值传递,有按引用传递,还有按共享传递。what?
先说结论
我一直认为,学习应该去刨根问底。抓住他的核心点,找到事物的本质。所以在这里从新给大家梳理下。
下面我先说下我的结论:
传递只有一种,就是
按值传递按值传递按值传递,重要的事情要讲三遍哦。
数据类型
标准规定,JavaScript 有 6种 基本类型,string number boolean null undefined symbol
和Object类型
我们首先来看下,这七种类型在内存中是怎么分配的。
堆栈结构
内存是分为栈区和堆区的

当我们声明6种基本类型的时候,就是在栈区开辟一段空间。
当我们声明一个Object类型的时候,在堆区和栈区各分配一段空间,堆区是Object声明的实体,栈区存放的是指向堆区的地址。
知识延伸
标准里面规定了,所有基本类型的值都是不可改变的。
var a = 1;
a = 2;
你可能说不对吧,上面的代码,a就从1变成2了。
对,上面的变量a,确实变了。要理解上面那句话,我们就需要明白 基本类型值本身 和 一个被赋值基本类型的变量的区别。值不可更改,变量可以重新赋值。
下面我们看一下,当我们赋值的时候,内存中是怎么工作的。

当我们声明 a 并且赋值为 1 的时候,栈区分配一段空间,值为1。
当我们赋值为2的时候,并不会修改 1 所在空间,而是从新分配一段空间,值为 2,并且把这个赋值给变量 a。
值传递
上面的东西我们都明白了,现在我们再来看看按值传递是怎么回事。
第一种情形
var a = 1;
function getNum(p){
p = 10;
return p;
}
getNum(a) // 10
a // 1
执行方法的时候,会把 a 的值,复制一份传递给形参 p。
由于 a 的值是 1 ,所以 p 的值也是 1。

第二种情形
var a = {
x: 1
}
function getNum(p){
p.x = 10;
console.log(p.x) // 10
}
getNum(a)
a.x // 10
以上代码也是把 a 的值复制一份给 p。
只不过 a 的值比较特殊,他是一个指向堆区的内存地址,所以p的值也是一个指向同一个堆区的地址。
所以操作 p 的时候 a 也会跟着变化。因为这是在操作同一份内存。

第三种情形
var a = {
x: 1
}
function getNum(p){
p = 10;
console.log(p) // 10
}
getNum(a)
a.x // 1
这个跟第二种也有点像,也是 a 的内存地址复制了一份给 p。
区别是对变量 p 从新赋值了。所以他的修改其实不会影响到 a 。

扩展 const
通过以上内容是不是就彻底搞清楚了值传递到底是怎么回事了。所有的传递方式其实都是按值传递,只不过有的值是基本类型,有的值是内存地址。
我们再来延伸一下,ES6 出了一个新的声明变量的方式 const。它可以声明一个常量,并且不能重新声明。也不能修改值。
我们来看下:
const a = 100;
a = 200; // error
const b = {x:100}
b = {} //error
b.x = 200; //修改成功
我们通过今天的内存只是来分析下:
其实上const 锁死的是变量 a,你在声明赋值之后他就锁定了那一块栈区的内存。当你赋值的时候其实就是换一块栈区内存空间,所以修改是无效的。
但是如果初始化值是一个对象,我们知道在栈区它是一个内存地址。虽然我们无法修改栈区,但是我们可以修改地址对应的堆区的内容。这也是最后b.x = 200能够修改成功的原因。
最后
当如果再有人问你 值传递 引用传递 的时候,你可以大声的告诉他,只有一种传递,值传递
啰里啰嗦的写了一堆,文笔不好望见谅。如果有错误或者不足之处,希望大家指正。如果你读了之后,对你有一点点的帮助,不甚荣幸。