一个原因这样做是为了让单击处理程序运行不结冰的页面。 JavaScript是单线程的。因此,如果点击处理程序(f
)需要很长时间才能执行(可能会进行一次计算,说明需要1分钟),则该用户在屏幕上执行的任何点击/操作都是added to a event queue。一旦点击处理程序完成,浏览器将执行这些操作。但在此之前,该应用对用户看起来没有反应 - 他点击了按钮,但似乎没有发生任何事情。
setTimeout
帮助,因为单击处理程序立即返回。回调f
被添加到事件队列中以在将来轮到运行,从而使浏览器有机会运行已经存在于队列中的任何事件(例如,被按下并且回来的按钮动画将有机会被处理;如果你有一个长时间运行的点击处理程序,该按钮将保持压低状态)。
虽然这是更好的,后面还有一个机会,当f
不会得到运行的页面将被冻结。避免这种情况的方法是让f
以小块进行工作,即做20ms的工作,然后用剩余工作的回调调用setTimeout。这样,在这20ms内堆积的任何其他事件都有机会运行。这样的功能可能如下:
something.click(safeWrap(function() {
function part1() { /* do something */ }
function part2() { /* do something */ }
part1();
safeWrap(part2);
}));
另一个理由这样做是为了不让单击处理程序泡沫可能发生高达点击方法是一个例外。也许,click
方法会自动捕获异常并在屏幕上显示它们,在这种情况下,我们希望忽略这个错误。这是可行的,因为f
在事件循环的另一个转角上运行,并且异常不会上升到click
,因为调用堆栈在新回合中以f
开头。唯一的例外将泡沫到click
方法,如果因为在这种情况下,处理不裹safeWrap
,f
会从click
最后一个常规的方法调用中,请注意单击处理安全包装得到所有甚至在click
方法传递给它的参数。 它确实不得到传递给safeWrap
额外的参数作为在对方的回答指出。这显然是有道理的,因为我们希望点击处理程序不关心处理程序是直接传递还是安全包装。此代码可能表明:
(function() {
console.clear();
function safeWrap(f) {
return function() {
setTimeout.apply(window, [f, 0].concat([].slice.call(arguments)));
};
}
var f = function(a) { console.log(a /* prints 2 */); throw Error("f"); };
function click(f) {
f(2);
}
click(safeWrap(f, 1));
})();
感谢您的回答,罗纳德。你能否详细说明为什么“.call”在“.slice”上被调用?我对这部分最困惑。 – 2009-06-21 12:50:44