for(var i=0;i<10;i++) {
setTimeout(function() {
console.log(i);
}, 300);
}
运 行它可以发现,连续输出了10个10.为什么呢?因为 “i” 这个变量在内存里只保存了一份。当调用settimeout方法时,往js事件线程的等待队列排入了十个执行函数,一直到每个300ms结束,这十个执行 函数才被调用,此时内存里的 “i” 这个变量经过十次循环最终值已变成了10 。所以就输出了10个10了。那怎么样输出0-9呢?我们可以用闭包把代码稍微改造下:
for(var i=0;i<10;i++) {
setTimeout((function(j) {
return function() {
console.log(j);
}
})(i), 300);
}
此 时就会输出我们想要的结果了,为什么?因为我们在给settimeout传参前,先使用闭包重新生成了一个执行函数,并且把当时循环里的"i"的值作为参 数传入到闭包函数中,结果函数无论什么时候执行,都打出的是当时被传入的那个值。由此说明:在给函数传参时,如果传入的参数是位于栈内存上的时候,会将栈 内存值复制一份传入到函数中。
那如果所传参数是个对象,也就是存在于堆内存上时会怎么办,我们接着做一个实验,这次的循环对象是一个对象字面量:
var obj = {j:0};
for(var i=0;i<10;i++) {
obj.j++;
setTimeout((function(o) {
return function() {
console.log(o.j);
}
})(obj), 300);
}
输出了什么?还是10个10!为什么?虽然还是使用闭包将当前循环的对象状态作为参数传入执行函数中,但传入的只是栈内存上的变量指针,位于堆内存上的内存仍然只有一份,所以这10个等待任务最终开始执行时,输出的都是同一份内存值。
由此我们得出几个最终结论:
1、对于基础数据类型,函数传参的时候会将变量值复制一份传入到函数中。
2、对于对象数据类型,函数传参时是将变量的指针复制一份,真正的变量值只有一份。
由于变量指针和基础类型变量都位于栈内存上,所以传参就是将栈内存复制一遍,堆内存不变。