鉴于正在学习 NodeJS,在学习过程中我会不定期的发一些 javascript 思考题来和大家共同学习探讨,一方面可以加深对知识点的理解,另一方面也可从不同的角度来分析问题,希望大家踊跃回答吧~ :)
有如下代码,希望能在回调中输出西游记中师徒四人的名字,可结果发现总输出最后一个人的名字:
function show(name, callback) {
process.nextTick(function() {
callback(name);
});
}
var names = ["唐僧", "悟空", "八戒", "沙僧"];
for(var index in names) {
var name = names[index];
show(name, function() {
console.log(name + "回来了");
});
}
// 输出结果:
// 沙僧回来了
// 沙僧回来了
// 沙僧回来了
// 沙僧回来了
试问应该怎么修改代码,使之正确输出?(show 方法不能改)
巴扎黑2017-04-10 17:53:06
很简单的一个修改方法是,将下面代码:
show(name, function() {
console.log(name + "回来了");
});
中的函数加一个参数name
即可。即
show(name, function(name) {
console.log(name + "回来了");
});
【2016.03.22补充】针对题主自己的回答的评论:
其实你的两种改法都用到了闭包。区别在于
第一种是用了show
函数与nextTick
包围的函数形成的闭包。
第二种很明显了,是用了你新增的那个函数与外部函数形成的闭包。
我觉得第一种改法更自然一下,因为原来的代码里面调callback
时传了参数,但却没有使用,所以加上name
是很合理的。而且本来的代码就能够形成闭包了,第二种改法却又在其他地方构造闭包,有点多余。
总结一下:要让结果运行正确,就必须要使数组中四个不同的元素各自保存到独立的作用域中。不论你的这两种改法还是通过ES6的let
,都是这个目的。只不过方法不同而已。
巴扎黑2017-04-10 17:53:06
典型闭包坑
for(var index in names) {
var name = names[index];
(function(name){
show(name, function() {
console.log(name + "回来了");
});
})(name);
}
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake
PHP中文网2017-04-10 17:53:06
补充:我也觉得楼主的题给错了,回调函数调用时都没传到参数,show的定义里是有的。
推特上看到的图,说的是setTimeout的情况,可以参考:
方法1:使用ES6 的let
声明
方法2:使用IIFE 传参,函数参数是按值传的,因此i
值保留
方法3:使用bind
绑定参数
阿神2017-04-10 17:53:06
// 方法一
for(var index in names) {
var name = names[index];
(function(name){
show(name, function() {
console.log(name + "回来了");
});
})(name);
}
// 方法二
for(var index in names) {
var name = names[index];
show(name, function(name) {
console.log(name + "回来了");
});
}
@hsfzxjy @manxisuo 其实我最想了解的就是这样两种方法的本质区别在哪里?如果方法二能解决这个问题,那么方法一所谓的闭包坑应该怎么去理解?只是方法二的另一种实现形式吗?
@lijsh 小胖你给出的是 ES6 的一些解法,确实很有参考价值,只是我还是没搞明白以上问题
迷茫2017-04-10 17:53:06
function show(name, callback) {
process.nextTick(function() {
callback(name);
});
}
改成
function show(name, callback) {
// process.nextTick(function() {
callback(name);
// });
}