如何避免JavaScript中的功能循环依赖关系?

时间:2018-12-18 17:11:57

标签: javascript design-patterns

有一个特殊的JavaScript模式一直困扰着我,我从来没有真正想出解决它的正确方法。相反,我通常只是忽略它,因为99%的JavaScript解释器都支持函数提升,因此不会出现运行时错误。

请考虑以下内容:

function onOpen()
{
    console.log("Connected!");
    ws.removeEventListener("open", onOpen);
    ws.removeEventListener("error", onError);
}

function onError()
{
    console.log("Failed to connect!");
    ws.removeEventListener("message", onMessage);
    ws.removeEventListener("error", onError);
}

var ws = new WebSocket("...");
ws.addEventListener("open", onOpen);
ws.addEventListener("error", onError);

在这段代码中,在onOpen函数中,我是在代码中onError的下方定义之前引用onError。这实际上不是问题,因为onOpen方法只有在定义了onError之后才能运行,但这仍然是一种不好的做法,会触发ESLint's no-use-before-define rule

从更一般的意义上讲,这是一个错误,只要存在两个需要互相引用的函数,就会出现该错误:

function a(x) {
    return x === 1 ? b(x) : 2;
}
function b(x) {
    return x === 2 ? a(x) : 1;
}

是否存在消除这种循环依赖性的设计模式?在我的简单通用示例中,简单的解决方案是“仅具有一个功能”:

function a(x) {
    return x === 1 ? 1 : 2;
}

但是,在绑定事件侦听器时,这似乎并不总是可能的。

2 个答案:

答案 0 :(得分:2)

  

这仍然是不好的做法,会绊倒ESLint's no-use-before-define rule

不,这不是一个坏习惯,这是非常必要的。当您具有循环功能依赖性时,这是首选模式。相应地配置ESLint(使用{ "functions": false })。

  

有消除这种循环依赖的设计模式吗?

不是。但是,有几种解决方法,例如使用var预先声明函数(足以make ESLint happy声明)。另外,您可以通过参数传递对该函数的引用:

function _a(x, f) {
    return x === 1 ? f(x) : 2;
}
function b(x) {
    return x === 2 ? _a(x, b) : 1;
}
function a(x) {
    return _a(x, b);
}

可以想到其他一些带有闭包的疯狂骇客,它们是Y组合器思想的后续行动。不过,这确实不适合您的事件侦听器方案。

答案 1 :(得分:1)

我认为这不是一个真正的问题,因为通常一切正常。另外,您还担心在声明onError之前就提到ws的事实,但是您并不关心提及var eventListenerListOnOpen = []; var eventListListenerOnError = []; var eventListListenerOnMessage = []; var ws; function removeListedEventListeners(object, eventName, eventListenerList) { eventListenerList.forEach(function(listener) { object.removeEventListener(eventName, listener); }); } function onOpen() { removeListedEventListeners(ws, "open", eventListenerListOnOpen); removeListedEventListeners(ws, "error", eventListenerListOnError); } function onError() { removeListedEventListeners(ws, "message", eventListenerListOnMessage); removeListedEventListeners(ws, "error", eventListenerListOnError); } ws = new WebSocket("..."); ws.addEventListener("open", onOpen); eventListenerListOnOpen.push(onOpen); ws.addEventListener("error", onError); eventListenerListOnError.push(onError); 的事实。但是,如果您仍然需要解决此问题,希望这种方法能成功。

{{1}}