今天阅读红宝书,在 JavaScript 的求值策略中,作者说所有的函数参数都是通过值传递,这里跟其他的语言很不一样,为此我特地查了一下英文原版:

All function arguments in ECMAScript are passed by value.
Professional JavaScript for Web Developers, 3rd Edition

书中确实是这样写的,但是 JavaScript 中的对象就是简单的按值传递(call by value)的么?执行这段代码:

function change(x, y) {
x.item = "changed"
y = {item: "changed"}
}
var obj1 = {item: "unchanged"}
var obj2 = {item: "unchanged"}
change(obj1, obj2)
console.log(obj1.item) //changed
console.log(obj2.item) //unchanged

如果是纯粹的按值传递,那么函数内部的修改不会影响外部对象,应该都输出 unchanged 的才对,如果是按引用传递,那么二者都应该保持 changed 才对。

确切的说,JavaScript 中的基本类型值(primitive type)是按值传递的,引用类型值(Object type)是 按共享传递(call by sharing) 的。

这里补充一下 JavaScript 的内存分配方式,基本类型值是在栈空间分配的内存,而引用类型值则是在堆空间分配的内存,然后在栈空间分配一个指向对象的指针。在把对象作为参数传入函数时,会将指向对象的指针进行复制然后传递过去(不是直接传递的指针!),这样当对对象进行修改时,会改变对象,这就是 obj1.item 被改变的原因,而 y = {item: "changed"} 则是改变了指针的指向,也就与原来的对象无关了,所以 obj1.item 未被改变。更多信息请查看ECMA-262-3 in detail. Chapter 8. Evaluation strategy.

那么反过来看红宝书错了么?红宝书在这句话之后进行了解释,作者认为传递拷贝之后的对象地址也是一种传值。

参考:

文档信息

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
本文链接:www.snovey.com/2016/09/call-by-value.html