1 前言

最近整理文件时发现了以前写的很多笔记,恰好想到Blog已经很长时间没更新了。

干脆把这些笔记再整理润色一下,搞点配图(当年一位大牛曾经说过:“我总算知道为什么我博客写的那么短了,因为图少”)慢慢都发出来吧。

今天是第一篇。

2 两个例子

先来看两个例子,选自《JavaScript 高级程序设计(第三版)》P70-71页,第四章-4.1节-4.1.3小节。

2.1 例子1 基本类型的参数传递

书上的解释是:JavaScript参数传递都是按值传递。

根据这个说法,传递给函数addTen的值是 20 ,所以函数结束时,原始变量count并不会发生改变。

2.2 例子2 引用类型的参数传递

这里产生了疑问:

  1. obj.name = "Nicholas";这一步操作给person添加了name属性并赋值Nicholas,在局部作用域中对对象的修改,反映在了全局上,这是按引用传递的么?
  2. 如果是按引用传递的,那么personname属性应该是Greg了,那么为什么最终personname属性还是那么personname属性呢?

3 理解参数传递

3.1 例子3 整合例子

为了解答前面的两个疑问,先把书上提供的例子整合修改一下。

最终的结果是:

结果有点出乎意料,那么先来了解一下什么是按值传递(传值调用 Call-by-value)和按引用传递(引用调用 Call-by-reference)。

3.2 传值调用

传值调用也被叫做按值传递,在传值调用中,传递给函数的参数是函数调用时实参的拷贝,在传值调用中实际参数被求值,其值被绑定到函数中对应的变量上。

通常的做法是把值复制到新的内存区域。

即例子3中函数changeAttribs的三个形参numobj1obj2是调用函数时传递的实参countperson1person2的拷贝。那么,无论numobj1obj2怎么变化,countperson1person2都保持不变。

问题来了,person1变了。

3.3 引用调用

引用调用也被叫做按引用传递,在引用调用中,传递给函数的参数是函数调用时实参的隐式引用而不是拷贝。

通常函数能够修改这些参数(例如赋值),而且改变对调用者是可见的。

即例子3中函数changeAttribs的三个形参numobj1obj2是调用函数时传递的实参countperson1person2的引用(一一对应指向同一块内存空间)。那么,函数内对numobj1obj2的任何修改都反映到countperson1person2上。

问题又来了,numobj2没有改变。

主要问题集中在JS的引用类型上面。

3.4 传共享调用

在传共享调用中,传递给函数的参数是函数调用时对象实参的引用的拷贝,也就是对象变量指针的拷贝。

即例子3中函数changeAttribs调用时实参中是对象变量的,形参得到的是对象变量指针的拷贝。也就是形参和实在指向了同一个对象,函数体内部对对象的修改对调用者可见。

4 代码分析

4.1 小动画解释

看个动图就懂了,觉得慢或者看不清楚的,往下走,有文字版。

4.2 文字配图解释

4.2.1 变量初始化

 

4.2.2 调用函数

4.2.3 执行函数体

如图所示:

变量 num 的值的改变,并不会影响到变量 count

变量 obj1 和变量person1 指向了堆内存中同一个对象,所以当执行到obj1.name = "bob";时,变量person1随之改变。

变量obj2重新赋值了,指向了函数体内创建的局部对象变量,所以修改obj2并不会对变量person2产生影响。

5 结论

从上面的分析,可以回答第二节提出的两个问题。

1.obj.name = "Nicholas";这一步操作给person添加了name属性并赋值Nicholas,在局部作用域中对对象的修改,反映在了全局上,这是按引用传递的么?

答:不是按引用传递的。

2.如果是按引用传递的,那么personname属性应该是Greg了,那么为什么最终personname属性还是那么personname属性呢?

答:因为不是引用传递,是传共享调用,函数的形参获得的是实参对象的指针的值的拷贝,指向了堆内存中的同一个对象。在函数体内声明的新对象赋值给形参时,形参实际上指向了新的对象,所以并不会影响到实参。

那么,对于JS来说:

  • 基本类型是传值调用
  • 引用类型是传共享调用

传值调用本质上就是对变量的值的拷贝。

传共享调用本质上是传递对象的指针的拷贝。

由于传递对象的指针本身也是值,所以传共享调用也可以当作传值调用,从这个角度上看,《JavaScript 高级程序设计(第三版)》上说: “JavaScript参数传递都是按值传递",是有道理的。

而《你所不知道JavaScript(中卷)》第二章 第28-29页中又说:“复合值——对象和函数,则总是通过引用复制的方式来赋值/传递。”

看来,目前JavaScript到底是什么参数传递方式,尚有争论,把实现机制搞明白就好了,叫名字并不重要。

与本文相关的文章
版权声明
转载保留版权: 大D综合研究院 | 《[笔记补完计划]理解JavaScript的参数传递》
本文链接地址:https://www.dadclab.com/archives/7609.jiecao
转载须知:如果您需要转载本文,请将版权信息,版权授权方式,以及本文的链接地址注明,谢谢合作。