按值传递和引用传递

337 阅读3分钟

欢迎大家关注我的 github

概述

最近在看到函数按值传递还是引用传递的时候产生了困惑,突然想到这个问题好像从第一次看红宝书就一直有这个困惑,于是各种搜,但是网上的种种回答也很不相同,想了两天终于算是有个理由可以说服自己了。我们先不讨论按值传递和引用传递的名字的定义,很多时候都是被名字的定义误导了。

实质重于形式,名字可以有很多,我们就不讨论到底该叫什么。只想说在现代的语言中参数的传递方式只有按值传递了,比如JS,没有引用传递。我当时比较困惑是将引用类型的复制当成了引用传递,准确的讲这应该叫引用访问,好像这是文字游戏的感觉。下面来简单的说一下。

基本概念

JS的值分为基本类型和引用类型

  • 基本类型:存储在栈中,因为占据的空间固定,存储在栈中更容易查找变量的值

  • 引用类型:存储在堆中,因为引用值大小会改变,所以不能放在栈中,不然会降低查询效率。但是可以将该引用值的地址存储在栈中,地址的大小是固定的。

当我们访问一个对象的时候,首先得到的是从栈中找到的该对象在堆内存中的地址,然后按照地址去获取对象中的值。

当我们复制基本类型变量和引用类型变量的时候

  • 基本类型复制:在栈中新开一个字段,将值复制到新字段,新值和旧值是两个独立的字段,互不影响

  • 引用类型复制:在栈中新开一个字段,将对象在堆内存中的地址(引用)复制到新字段,新值和旧值指向堆内存中的同一个对象,当我们要操作变量的时候,操作的是变量的引用。当给对象添加属性的时候,操作的是堆内存中实际的对象

基本类型的复制:

引用类型的复制:

传递参数

当我们传递引用类型的参数时,就是引用类型的复制,函数的形参还是和实参指向了同一个引用类型,此时操作变量是操作的变量的引用,当给对象添加属性的时候,操作的是堆内存中实际的对象。我们可以认为是按地址传递

当我们操作变量的时候,实际是:

所以我们就可以理解下面的这些代码了:

function setName(obj) {
    obj.name = 'zhangsan'   // 操作的是堆内存中的对象
}

var person = new Object()
setName(person)
console.log(person.name)  // zhangsan

function setName(obj) {
    obj.name = 'zhangsan'
    obj = new Object()      // 操作的是对象的引用
    obj.name = 'lisi'
}

var person = new Object()
setName(person)
console.log(person.name) // zhangsan

所以说函数是按值传递参数,JS没有引用传递



参考资料:

《JS高级程序设计》

https://www.zhihu.com/question/27114726

https://www.zhihu.com/question/51018162