【问题标题】:Anonymous function references in JavascriptJavascript 中的匿名函数引用
【发布时间】:2011-01-06 13:37:21
【问题描述】:

我目前正在为我正在进行的项目构建一个非常简单的 Observer 类。我已经成功实现了订阅、取消订阅和通知方法。使用“常规”函数(即:var f = function())时,一切都按预期工作。

但是,当我将匿名函数传递给 subscribe 方法然后尝试取消订阅传递“相同”匿名函数时,它(如预期的那样)不会从我的数组中删除该函数(毕竟它们是不同的)。

这是我的订阅和取消订阅方法:

this._subscribers = {};
subscribe: function(type, callback) {
    if ( isUndefined(this._subscribers[type]) ) {
        this._subscribers[type] = [];
    }
    this._subscribers[type].push(callback);
},
unsubscribe: function(type, callback) {
    if ( this._subscribers[type] instanceof Array ) {
        var index = this._subscribers[type].indexOf(callback);
        if ( index >= 0 ) {
            this._subscribers[type].splice(index, 1);
        }
    }
},

这是我正在测试的代码:

var o = new gaf.events.Observable();
o.subscribe('testEvent', function(event) { alert('Got It!'); });
o.notify('testEvent');
// Correct alerts 'Got It!'
o.unsubscribe('testEvent', function(event) { alert('Got It!'); });
o.notify('testEvent')
// Incorrectly alerts 'Got It!'

我知道我可以使用一个对象(即:_subscribers[event] = {}),然后当订阅某些东西时,我可以添加一个等于回调的新属性和等于回调的值。这将导致 Javascript 将回调转换为字符串。然后我可以使用该字符串查找它(前提是在 sub/unsub 中传递的方法完全相同)。

但是,这是一个移动项目,我对存储可能长达数百个字符的字符串非常谨慎,因为我们最终可能会有很多订阅者。

还有其他方法可以做到这一点吗?是否有任何 SMALL(小,甚至)散列库我可以用来散列函数的字符串值并将其用作属性?将回调的字符串值(以便我可以与它进行比较)存储在数组(而不是实际的回调)中并在其上使用eval() 会更好吗?

编辑

首先,感谢大家的回复!

关于“为什么还要传递匿名”函数的所有问题 -

确实没有理由不能使用命名函数。事实上,我同意每个人的观点,命名函数将是更好的解决方案。我只是在收集信息并寻找解决方案,这样我就可以构建一个尽可能最好地处理大多数场景的实现。

另一个原因是如果这个 Observable 类的用户(同事)向它传递了一个匿名函数然后取消订阅,会发生什么情况。该功能实际上不会被取消订阅,因此不会被清理。我反对孤立数据:)

也许我应该按原样提出另一个问题,是否可以测试回调是否是匿名的?我假设没有,但问一下也无妨。

【问题讨论】:

  • 作为一个移动项目,我不认为你想通过复制匿名函数(一个订阅和一个取消订阅)来浪费空间。此外,您还需要散列库的开销(空间和性能)。散列库需要对函数进行“字符串化”,然后对其进行散列。 “stringify”操作需要正确地使用空格。这一切听起来让人头疼。我建议遵循将函数保存到变量或使用命名函数的最佳实践。
  • 感谢您的评论!这就是为什么我不直接实现散列函数的原因。移动设备上该过程的开销和复杂性吓到我了。

标签: javascript dom-events anonymous-function mobile-website


【解决方案1】:

存储整个字符串并没有错;过早的优化是邪恶的。

但是,这听起来是个非常糟糕的主意。
如果有人更改了功能,但忘记更改未订阅的副本,则代码将被巧妙地破坏而没有任何警告。

如果用户想取消订阅,您可以要求用户将匿名函数存储在变量中。
或者,您可以为每个订阅者传递一个可选名称,然后按该名称取消订阅。

【讨论】:

  • 感谢您的洞察力!我同意存储字符串完全是一个坏主意。我只是抛出了我对此事的一些想法:)
【解决方案2】:

使用观察者的客户端应该存储对函数的引用。

   var obsCallback = function() {

   }
   o.subscribe('test', obsCallback);
   ....
   o.unsubscribe('test', obsCallback);

换句话说,保持对函数的引用...

【讨论】:

    【解决方案3】:

    也许更好的解决方案是使用您的库修改代码

    var f = function() { alert('Got It!'); };
    o.subscribe('testEvent', f);
    o.notify('testEvent');
    o.unsubscribe('testEvent', f);
    o.notify('testEvent');
    

    你甚至可以从 subscribe 方法中返回函数

    var f = o.subscribe('testEvent', function() { alert('Got It!'); });
    // ...
    

    如果你想为订阅的函数存储一个哈希或其他标识符,它对调用代码是不透明的,这意味着你只需使用返回的值来取消订阅,并且库隐藏了实现细节。

    【讨论】:

      【解决方案4】:

      传递匿名函数而不是命名函数的原因是什么,或者保留可用于以后取消订阅的引用的原因是什么?

      或者,您可以允许一个可选的“id”参数,但这需要不必要的复杂簿记以避免重复。

      【讨论】:

        猜你喜欢
        • 2011-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-24
        • 2010-11-06
        • 2011-03-02
        • 2011-03-11
        • 1970-01-01
        相关资源
        最近更新 更多