提问人:Lawrence 提问时间:11/9/2008 最后编辑:Shadow Wizard Is Sad And AngryLawrence 更新时间:5/20/2016 访问量:33107
Javascript 回调函数和递归
Javascript callback functions and recursion
问:
这是一个脑筋急转弯的问题,因为代码按原样运行良好,它只是稍微刺激了我的审美意识。我正在转向 Stack Overflow,因为我自己的大脑现在正在让我失望。
下面是一段代码,它使用 Google Maps JS API 查找地址并在地图上放置标记。但是,有时初始查找会失败,因此我想使用不同的地址重复该过程。
geocoder.getLatLng(item.mapstring, function(point) {
if (!point) {
geocoder.getLatLng(item.backup_mapstring, function(point) {
if (!point) return;
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
})
return;
}
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
})
(第二个参数是回调函数。getLatLng
当然,您可以看到居中和缩放地图并添加标记的三条线是重复的,一次在主回调中,一次在“回退回调”(哈哈)中。你能找到一种方法来表达整个事情而没有任何冗余吗?如果你的解决方案适用于任意数量的备份映射字符串,你将获得奖励积分和我的赞美。
答:
这个怎么样?
function place_point(mapstrings,idx)
{
if(idx>=mapstrings.length) return;
geocoder.getLatLng(mapstrings[idx],
function(point)
{
if(!point)
{
place_point(mapstrings,idx+1);
return;
}
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
});
}
任意数量的备份字符串。第一次用 0 作为第二个参数调用它即可。
是的,将其分解为函数:)
geocoder.getLatLng(item.mapstring, function(point) {
if (!point) {
geocoder.getLatLng(item.backup_mapstring, function(point) {
if (point) {
setPoint(point);
}
})
return;
}
function setPoint(point) {
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
}
setPoint(point);
});
有一种非常好的方法可以在不显式支持递归的语言结构中执行递归,称为定点组合器。最著名的是 Y-Combinator。
function Y(le, a) {
return function (f) {
return f(f);
}(function (f) {
return le(function (x) {
return f(f)(x);
}, a);
});
}
这看起来有点吓人,但你只需要写一次。使用它实际上非常简单。基本上,你把一个参数的原始 lambda 变成一个由两个参数组成的新函数 - 第一个参数现在是你可以进行递归调用的实际 lambda 表达式,第二个参数是你想要使用的原始第一个参数 ()。point
这就是您在示例中使用它的方式。请注意,我使用字符串列表进行查找,pop 函数会破坏性地从头部删除元素。mapstrings
geocoder.getLatLng(pop(mapstrings), Y(
function(getLatLongCallback, point)
{
if (!point)
{
if (length(mapstrings) > 0)
geocoder.getLatLng(pop(mapstrings), getLatLongCallback);
return;
}
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
});
其他答案都很好,但这里还有一个选择。这允许您保留开始时使用的相同形式,但使用命名 lambda 函数的技巧,以便您可以递归引用它:
mapstrings = ['mapstring1', 'mapstring2', 'mapstring3'];
geocoder.getLatLng(mapstrings.shift(), function lambda(point) {
if(point) {
// success
map.setCenter(point, 13);
map.setZoom(7);
map.addOverlay(new GMarker(point));
}
else if(mapstrings.length > 0) {
// Previous mapstring failed... try next mapstring
geocoder.getLatLng(mapstrings.shift(), lambda);
}
else {
// Take special action if no mapstring succeeds?
}
})
第一次使用符号“lambda”时,是将其作为新的函数文字名称引入。第二次使用它时,它是一个递归引用。
函数文字命名在 Chrome 中有效,我认为它适用于大多数现代浏览器,但我没有测试过它,也不了解较旧的浏览器。
评论