JavaScript深入之参数按值传递

222 阅读7分钟


JavaScript深入系列第九篇,除了按值传递、引用传递,还有第三种传递方式 —— 按共享传递

定义

在《JavaScript高级程序设计》第三版 4.1.3,讲到传递参数:

ECMAScript中所有函数的参数都是按值传递的。

什么是按值传递呢?

也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。

按值传递

举个简单的例子:

var value = 1;
function foo(v) {
    v = 2;
    console.log(v); //2
}
foo(value);
console.log(value) // 1复制代码

很好理解,当传递 value 到函数 foo 中,相当于拷贝了一份 value,假设拷贝的这份叫 _value,函数中修改的都是 _value 的值,而不会影响原来的 value 值。

引用传递

拷贝虽然很好理解,但是当值是一个复杂的数据结构的时候,拷贝就会产生性能上的问题。

所以还有另一种传递方式叫做按引用传递。

所谓按引用传递,就是传递对象的引用,函数内部对参数的任何改变都会影响该对象的值,因为两者引用的是同一个对象。

举个例子:

var obj = {
    value: 1
};
function foo(o) {
    o.value = 2;
    console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2复制代码

哎,不对啊,连我们的红宝书都说了 ECMAScript 中所有函数的参数都是按值传递的,这怎么能按引用传递成功呢?

而这究竟是不是引用传递呢?

第三种传递方式

不急,让我们再看个例子:

var obj = {
    value: 1
};
function foo(o) {
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1复制代码

如果 JavaScript 采用的是引用传递,外层的值也会被修改呐,这怎么又没被改呢?所以真的不是引用传递吗?

这就要讲到其实还有第三种传递方式,叫按共享传递。

而共享传递是指,在传递对象的时候,传递对象的引用的副本。

注意: 按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!

所以修改 o.value,可以通过引用找到原值,但是直接修改 o,并不会修改原值。所以第二个和第三个例子其实都是按共享传递。

最后,你可以这样理解:

参数如果是基本类型是按值传递,如果是引用类型按共享传递。

但是因为拷贝副本也是一种值的拷贝,所以在高程中也直接认为是按值传递了。

所以,高程,谁叫你是红宝书嘞!

下一篇文章

JavaScript深入之call和apply的模拟实现

深入系列

JavaScript深入系列目录地址:github.com/mqyqingfeng…

JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。

关注下面的标签,发现更多相似文章
冴羽lv-5前端
发布了 68 篇专栏 · 获得点赞 15,082 · 获得阅读 453,897
关注
安装掘金浏览器插件
打开新标签页发现好内容,掘金、GitHub、Dribbble、ProductHunt 等站点内容轻松获取。快来安装掘金浏览器插件获取高质量内容吧!
评论

前端
那是不是就可以理解为: 共享传递是指对对象的一层拷贝呢?
8月前
回复
回复 : 拷贝对象的引用,个人理解为:内存中有两个变量(外部的和函数的参数)持有对象的引用地址。
7月前
回复
感觉可以理解为基本类型是复制值传递 复杂类型为复制指针传递
11月前
2
回复
前端
回复 : 红宝书上面就是酱紫说的
10月前
回复
回复 : 其实复杂类型的变量保存的值不就是引用吗 对象保存在栈中,引用指向栈中的对象
7月前
回复
走在路上的蜗牛
尴尬
2年前
回复
Front-End Sonftware Engineer @ Acxiom
我以前是这么理解的,arguments 中的参数若为对象,则是直接将其以指针形式传入,否则外部调用的 function 怎么能改变 obj 内部属性的值呢,除非他们在操作用一个内存地址。
2年前
回复
Front-End Sonftware Engineer @ Acxiom
回复 : 但现在看来,这个观点无法解释为什么当传入参数为 obj,然后直接修改 obj = 2, 为什么没有去修改 obj 的值
2年前
回复
大四 @ bytedance
回复 : 这个其实就是C语言里的指针,只不过JS高程里没有刻意的去区分指针和引用。 传递obj的时候,参数复制了obj 的指针, 所以obj和参数都指向同一内存,但是参数只是obj指针的一个副本,所以修改参数只会改变参数的指向,不会影响原来的obj
1年前
1
回复
《JS正则迷你书》作者
看了辩论以后,理解了双方的出发点。说下个人看法。对于读者而言, 1. 如果你的第一门语言,就是 js,完全可以忽略高程中这句话,徒增纠结。 2. 如果你之前学过 java,js 的传参策略与 java 是一样的,因为它也是按值传递。 3. 如果你是从 c++,你自然就懂,什么叫按值传递,什么叫按引用传递。 4. 从 c++ 转 java 时,你也会遇到同样的问题。换句话说,你可以看着 java 为啥是按值传递的文章来理解这句话,前提是你纠结这件事情。 5. 变量是盒子,有时放的是值,有时放的指针。 6. 按值传递,传递的是变量里内容,不管盒子里是啥。 7. 按引用传递,在 c++,变量也有地址,参数变量接受的是变量的地址,js 中没有此东西,就算你知道了什么是按引用传递对你学 js 也没有更大的帮助。 8. 作者说这句话的目的,都明确的说了,是区别其他后台语言。而我们初学者容易误以为按引用传递中的引用是引用类型的引用。 9. 高设的两大 “罪过”,此是其一,加上一句,“如果读者没 c++,就不用看这段。” 就好了。 10. 另一大罪过是,闭包不要轻易使用,占用内存。加上一句,“没必要创建闭包时,就别创建了。有必要时,那就随便使用,当然不再使用后,要置引用为 null,释放内存。当然,你不在乎那点内存,也没关系”。 总结,作者随便的两句话,掀起了多大风波。艾玛~
收起
2年前
11
回复
冴羽lv-5(作者)
前端
回复 : 好巧,我在看你的递归系列文章~~~ 赞~
2年前
回复
Coder
只有按值传递吧。只不过引用类型传的是引用,第二第三种传递都是一样的。第二种是把函数内的引用修改了,指向了其他地方,原来的值并没变。第三种通过引用修改了原来的值。
2年前
1
回复
冴羽lv-5(作者)
前端
回复 : 如果把引用也当成值的一种,自然都是按值传递,两种说法都有,仁者见仁。第二种第三种并不一样,第二种传的是引用,在引用传递下,o = 2 的赋值会更改外层的 obj,但是 js 不使用引用传递,所以无法验证这一点。第三种传递的是引用的副本,是拷贝了一份引用,然后修改的是这个副本,所以不会影响外层的值。就是因为尼古拉斯认为引用也是值的一种,才会说 CMAScript 中所有函数的参数都是按值传递的。
2年前
回复
查看更多 >