2009-06-21 111 views
1

我碰到这个片段在内部网站上跑了,但我无法理解它:这段JavaScript代码做了什么?

function safeWrap(f) { 
    return function() { 
    setTimeout.apply(window, [f, 0].concat([].slice.call(arguments))); 
    }; 
} 

后来,它的使用是这样的:

// Set click handler. 
(...).click(safeWrap(function() { ... })); 

这是什么意思做?

回答

11

safeWrap返回调用时设定的0毫秒超时功能(点击触发的事件)。

如果safeWrap函数传递大于f更多的参数,这将增加这些以函数f的参数列表。

那只是对提供的代码的解释。所以我不能说出它究竟是做什么的......例如,这个代码在哪里使用?

+0

感谢您的回答,罗纳德。你能否详细说明为什么“.call”在“.slice”上被调用?我对这部分最困惑。 – 2009-06-21 12:50:44

0

一个原因这样做是为了让单击处理程序运行不结冰的页面。 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方法,如果因为在这种情况下,处理不裹safeWrapf会从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)); 

})();