看到一个这样的问题javascript
function setName(obj) { obj.name = "Tom"; obj = new Object(); obj.name = "Greg" ; } var person = new Object(); setName(person); alert(person.name); //"Tom"
按照通常的理解第二次重写name属性会覆盖原先的name,输出应该是"Greg"才对。
这个问题其实就是高程书上的原题,高程书的一些地方看了不少遍,可是每次重看都仍是会有新的理解,这里理解的关键在于JavaScript函数的参数是按值传递的。
以前只是粗泛的了解参数按值传递,可是却并无搞清楚本质,结合高程书和知乎的这个回答连接才对这个问题算是真正理解了。java
简单类型的值,它们的值直接存储在变量访问的位置,这是由于这些简单类型占据的空间是固定的,因此可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。每次复制都是一个单独的副本,之间相互独立git
var num1 = 5; var num2 = num1;
图示github
引用类型的值存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。这是由于:引用值的大小会改变,因此不能把它放在栈中,不然会下降变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,因此把它存储在栈中对变量性能无任何负面影响。函数
var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); //"Nicholas"
图示性能
其实ECMAScript函数参数其实是函数的局部变量,所以本题中将一个对象做为参数传入时,复制了一个指针指向对象在堆内存中的内存地址。按值复制传递上复制了一个指针变量,这个变量是按值传递的。
在调用函数内部将一个新的Object赋给obj以前内存状况是这样的:
若是是按引用传递,就会直接把第一个(也就是变量自己)整个传递进去(就不会有第二格的存在了)
再将新的Object赋给obj以后:
能够看到obj指向新的Object内存地址,而person引用的仍然是原来的Object,而且在函数内部新建的局部对象会在函数执行完毕后销毁,所以打印的name是第一个。
原文地址spa
《JavaScript高级程序设计》设计