之前在segmentfault回答了一个问题,关于JavaScript函数参数传值还是传引用。可能是受到C++的影响,一开始我得出的结论是:基本类型传值,引用类型传引用。而且解释的时候甚至还谈到了编译时什么子程序调用,保存现场,返回现场等内容。然而很快就被打脸了,因为我自己也发现了貌似不能自圆其说,然后被一位热心得同学指正了,十分感谢。
只是我有点不理解,但是胡乱猜测只是浪费时间而已。一言不合还是得拿出《JS高程》来看看。然后才发现其实上面写的非常清楚:函数参数都是传值的,只是基本类型传的是value,引用类型传的是address。
其实在这个问题中我忽略了一个关键的因素,那就是函数的参数是一个怎么样的存在?其实函数的参数就相当于在函数内部声明的一个临时变量,只是这个临时变量被赋予了外部传递个参数的值(可以使value或address),而函数内对参数的引用首先会扫描函数作用域内是否存在这个变量,然后在查找上一级作用域。也就是函数内部操作的其实只是这个局部变量。
另外一个关键的点就是要弄明白变量声明和赋值的过程。在声明一个引用类型的时候,首先创建一个变量,将这个变量加到作用域中,然后申请内存存放等式右边的值,而这个变量的指针就指向这个值得地址。就完成了变量声明并赋值的过程:
举个例子:
function (){
var obj = { item : 'value' };
}
首先 创建一个变量 obj ,并为其申请内存,只是此时内存为空,然后将其加入到 function 的局部作用域中,然后申请内存存放 { item:‘value ‘} ,假设 { item:‘value’}的存放地址为 #abcdef,然后obj 的指针指向 {。。。 },其实也就是把 {。。。} 的地址值赋值给 obj ,那么 obj 的值就变成了 #abcdef。过程结束。然后函数结束的时候销毁局部变量,回收内存。
其实到这里还有点疑问,那就是在局部作用域 中申请的内存什么时候会被回收什么时候回继续保留下来。举个栗子:
var obj;//global obj
function f(obj1){
//相当于 var obj1 = obj ;
obj={item:'value'};
obj1={item:'value change'};
}
f(obj);
alert(obj.item);// "value"
根据上面的描述,输出这个结果是可以理解的,疑问就是为什么 { item:‘value’}所在的内存区域函数结束的时候没有被系统回收而 { item:”value change”} 所在的内存区域则被垃圾回收系统回收了。
答案是 {item:‘value’} 所在的区域已经被加入到全局作用域当中,而后者则只是局部作用域,所以在局部作用域被销毁(函数结束)的时候内存被系统回收了。
由于 obj 是全局变量,而obj的指针指向{item:‘value’},也就是被obj引用了,在系统进行局部作用域内存回收的时候回收标记被清除;而obj1 以及{item:‘value change’}则是都是局部的作用域属性,在作用域被销毁的时候连同它们也一起被销毁了,内存被回收。
Ps:上述内容纯属个人理解,有错之处还望指正。
P.Ps:跟人讨论好处真是挺多的,没想到一个小问题想了这么多。