传递只有一种 —— 按值传递

488 阅读4分钟

值传递应该是JS最基本的一个知识点了,网上也有很多说法,有按值传递,有按引用传递,还有按共享传递。what?

先说结论

我一直认为,学习应该去刨根问底。抓住他的核心点,找到事物的本质。所以在这里从新给大家梳理下。

下面我先说下我的结论:

传递只有一种,就是 按值传递 按值传递 按值传递,重要的事情要讲三遍哦。

数据类型

标准规定,JavaScript 有 6种 基本类型,string number boolean null undefined symbolObject类型

我们首先来看下,这七种类型在内存中是怎么分配的。

堆栈结构

内存是分为栈区和堆区的

当我们声明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能够修改成功的原因。

最后

当如果再有人问你 值传递 引用传递 的时候,你可以大声的告诉他,只有一种传递,值传递

啰里啰嗦的写了一堆,文笔不好望见谅。如果有错误或者不足之处,希望大家指正。如果你读了之后,对你有一点点的帮助,不甚荣幸。

参考链接

developer.mozilla.org/zh-CN/docs/…