首页 > 如何理解JavaScript函数中的传递参数?

如何理解JavaScript函数中的传递参数?

做前端开发有一段时间,对一些相关概念有了一定的认识。但是,秉着“勿在浮沙筑高台”的态度,我又返回来看了看《JavaScript高级程序设计》这本书。
在看到“变量、作用域和内存问题”这一章时,书中说到:因为访问变量有按值和按引用两种方式,而参数只能按值传递。

示例:

function setName(obj){
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Grag";
}
var person = new Object();
setName(person);

alert(person.name); //"Nicholas"

其实这个示例,我觉得已经说明了参数的传递的确是按值来传递的,哪怕是一个对象。
但我还是不太明白其中的原理,想象不出在内存中是怎样的。
还望,前端高手能够帮忙解读,解读。


obj开始只是person的一个副本,正如其他引用类型的复制一样,它和person指向的是同一地址,但obj = new Object();之后,obj指向了另一块内存,与person断开了联系,这就是重写。如果没有obj = new Object();这一句,alert(person.name);的结果将会是Grag


@hiYoHoo 的图已经很简明了,我再来介绍一下 obj 和 person 的本质区别,以下描述比较粗浅,而且比较冗长,见谅,如有遗漏之处请指正。

在 JavaScript 中,从内存分配角度来看,有两种类型的变量:

  1. 在栈空间分配的变量(所有值为 number、boolean、string、null、undefined、Symbol 的变量)

  2. 在堆空间分配的变量(所有值为数组、对象、函数的变量)
    谨记以上两种类型,下面称他们为栈变量和堆变量

那么什么是堆、什么是栈?

从前一座城市,每个出生的人都能分配到一间占地面积固定的房(占用空间已知),每间房子都有它的值(里面居住的人已知)和它的地址(街道门牌号已知),如果一个住户从来不搬家,那么街道办就非常好管理所有的定居居民,办事效率会非常高。

// 创建一个 num 数字,相当于给居民分了一套房子
// 变量的内存空间是确定的,4 个字节
// 变量还有个隐含的内存地址,你根据这个地址就可以找到变量 num,取得值 1
var num = 1

// 现在要去找到这个房子,把里面的值打印出来
// 第一件事就是寻址,是编译器帮你自动计算完成的
console.log(num)

从前又有一个游牧民族,这个游牧民族会因为人口、资源、环境等原因迁徙,他们没有固定面积的住房,也不知道他们随时会迁徙到哪里,既然连地址都不知道,更别提想要知道他们里面有哪些人了。这就给城邦的街道办带来了管理麻烦,虽然游牧民族很灵活,他们的生存活动范围可以随意扩大,但是他们经常迁徙,地址经常变动,所以街道办采用的管理策略是:每次游牧民族一迁徙,就给街道办寄一封信,信里面只写着游牧民族首领家的地址,街道办就带人先找到首领家,然后再由首领带领,依次找到所有人的家。

// 创建一个 arr 数组,相当于出现了一个游牧民族
var arr = [1, 2, 3, 4]

// 游牧民族增加了新成员
arr.push(5)

// 游牧民族兼并了别的族群
arr.concat([6, 7, 8, 9])

// 因为堆变量在不停地改变空间大小,所以不能跟栈变量生活在一起,会挤死人的
// arr 就是他们的首领,arr[0] 就是第一户,arr[1] 就是第二户
// 所以只要找到首领,就找到所有人了

// 首领聘了一个助理,帮他一起管理所有人
var helper = arr

// 第一户的人长大了
helper[0]++

// helper 叛变了,他现在去另一个游牧民族了
helper = new Array(10)
helper[0]++ // NaN!!!,暴毙身亡

// 栈变量的寻址是编译器帮你完成的
// 而堆变量,寻址是我们自己手动完成的,这就是通过首领,来寻找剩下的所有人的过程
// 首领就是一个`引用`型变量

来看你的代码:

function setName(obj) { // obj 是首领的助理
    // 助理帮首领管理 name 家的人
    obj.name = "Nicholas";

    // 助理叛变了
    obj = new Object();

    // 助理管理另外一个游牧民族的名为 name 家的人
    obj.name = "Grag";
}

// person 就是首领
var person = new Object();

setName(person);

alert(person.name); //"Nicholas"
【热门文章】
【热门文章】