2014-11-08 38 views
4

我发现这个在D3:什么是JavaScript中的“防卫参考”?

function d3_dispatch_event(dispatch) { 
    var listeners = [], 
     listenerByName = new d3_Map; 

    function event() { 
    var z = listeners, // defensive reference 
     i = -1, 
     n = z.length, 
     l; 
    while (++i < n) if (l = z[i].on) l.apply(this, arguments); 
    return dispatch; 
    } 

    event.on = function(name, listener) { 
... 

Link to a line on github

什么是 “防御性的参考” 这里的意思?

+0

它在JavaScript中没有意义。它只对该评论的作者有意义。 – 2014-11-08 18:24:24

+0

@squint漂亮的渔获(#58线)。让我接受你的答案,如果你会创建它,并删除此评论:)。 – Leviathan 2014-11-08 18:55:04

+1

我对此并不完全正确。看起来这条线是为了去除。虽然它可以确保删除项目仍然可以访问(我想这就是他们想要的),[第63行](https://github.com/mbostock/d3/blob/master/src/event/dispatch.js# L63)确实在'listeners'中执行'.push()'。但是,由于他们为'while'循环缓存'.length',所以添加的项目将不会被触及。所以我想这只是为了删除,以便删除的处理程序仍然会触发。继续并给出答案。我不确定我是否分析过所有角度,我需要重新开始工作! :-) – 2014-11-08 19:03:33

回答

2

实现事件系统时的问题是激发侦听器的权利。调用回调时更改侦听器列表可能会导致意外的行为。

例如,假设有2个听众收听事件x。事件x被触发,系统开始将听众从index 0循环到index 1。听众index 0首先被解雇。回调被调用,并且回调发生在index 1删除监听器。循环将继续进行,并尝试在index 1处触发回叫。监听器不再存在,并发生异常。

这是真正简化的例子,当在循环时更改侦听器列表时可能会出错。通过在循环之前计算侦听器的数量,可轻松处理列表中的添加项。处理循环移除操作要复杂得多。

在d3中,每个从侦听器列表中移除动作都将导致创建新的侦听器数组,并将该数组作为变量listeners的值。

listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); 

侦听器的原始数组未被更改,因此循环将工作,但对原始数组的引用将丢失。这就是为什么event函数会创建对其的本地引用。通过defensive reference作者可能意味着创建listeners当前(循环前)值的临时引用,即使listeners的值在循环的某个点发生更改也是如此。