Javascript 中的匿名函数引用
我目前正在为我正在从事的项目构建一个非常简单的观察者类。我已经成功实现了订阅、取消订阅和通知方法。使用“常规”函数(即:var f = function()
)时,一切都完全按照预期工作。
但是,当我将匿名函数传递给订阅方法,然后尝试取消订阅传递“相同”匿名函数时,它(如预期)不会从我的数组中删除该函数(毕竟它们是不同的)。
这是我的订阅和取消订阅方法:
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 中传递的方法完全相同)。
然而,这是一个移动项目,我对存储可能有数百个字符长的字符串作为属性非常谨慎,因为我们最终可能会拥有很多订阅者。
还有其他方法可以做到这一点吗?是否有任何小型(微小的,甚至)散列库可以用来散列函数的字符串值并将其用作属性?将回调的字符串值(以便我可以与它进行比较)存储在数组(而不是实际的回调)中并对其使用 eval()
会更好吗?
编辑
首先,感谢大家的回复!
根据有关“为什么要传递匿名”函数的所有问题 -
确实没有理由不能使用命名函数。事实上,我同意每个人的观点,命名函数将是更好的解决方案。我只是收集信息并寻找解决方案,以便我可以构建一个尽可能最好地处理大多数场景的实现。
造成这种情况的另一个原因是,如果此 Observable 类的用户(同事)向其传递一个匿名函数然后取消订阅,会发生什么情况。该函数实际上不会被取消订阅,因此不会被清理。我对孤立数据有意见:)
也许我应该提出另一个问题,是否可以测试回调是否是匿名的?我假设不会,但问一下也没什么坏处。
I'm currently in the process of building out a VERY simple Observer class for a project I'm working on. I have successfully implemented the subscribe, unsubscribe, and notify methods. Everything works exactly as expected when using "regular" functions (i.e: var f = function()
).
However, when I pass an anonymous function to the subscribe method and then try to unsubscribe passing the "same" anonymous function it (as expected) doesn't remove the function from my array (they are different, after all).
Here's my subscribe and unsubscribe methods:
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);
}
}
},
And here's the code I'm testing with:
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!'
I know I could using an object (i.e.: _subscribers[event] = {}
) and then when something subscribes I could add a new property equal to the callback and the value equal to the callback. This will cause Javascript to convert the callback to the string. I could then look it up (provided the methods passed in sub/unsub are exactly the same) using that string.
However, this is a mobile project and I'm very leery about storing strings that could be hundreds of characters long as properties as we could end up with a lot of subscribers.
Are there any other ways of doing this? Are there any SMALL (tiny, even) hashing libraries I can use to maybe hash the string value of the function and use that as the property? Would it be better to store the string value of the callback (so I can compare against it) in the array (rather then the actual callback) and use eval()
on it?
EDIT
First, thanks all for the replies!
Per all the questions about "Why even pass anonymous" functions -
There really is no reason one COULDN'T use named functions. In fact, I agree with everyone that named functions are going to be the better solution. I'm simply gathering information and looking for a solution so that I can build out an implementation that handles the most scenarios as best as possible.
The other reason for this is what happens if a user (co-worker) of this Observable class passes it an anonymous function and then unsubscribes. That function won't actually be unsubscribed and therefore won't be cleaned up. I have a thing against orphaned data :)
Maybe another question I should as is, is it possible to test if the callback is anonymous or not? I'm going to assume no but doesn't hurt to ask.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
存储整个字符串并没有什么问题;过早的优化是邪恶的。
然而,这听起来是一个非常糟糕的主意。
如果有人更改了该功能,但忘记更改未订阅的副本,则代码将被巧妙地破坏,而不会发出任何警告。
相反,如果用户想要取消订阅,您可以要求用户将匿名函数存储在变量中。
或者,您可以向每个订阅者传递一个可选名称,然后通过该名称取消订阅。
There is nothing wrong with storing the entire string; premature optimization is evil.
However, this sounds like an incredibly bad idea.
If someone changes the function, but forgets to change the unsubscribed copy, the code will be subtly broken with no warning whatsoever.
Instead, you can require the user to store the anonymous function in a variable if they want to unsubscribe from it.
Alternatively, you can pass an optional name with each subscriber, then unsubscribe by that name.
使用观察者的客户端应该存储对该函数的引用。
换句话说,保留对周围函数的引用......
the clients that use the Observer should store the reference to the function.
in other words, keep a reference to the function around...
也许更好的解决方案是使用您的库修改代码
您甚至可以从订阅方法返回函数
,然后如果您想存储订阅函数的哈希或其他一些标识符,它对调用代码是不透明的,这意味着您只需使用要取消订阅的返回值,并且库隐藏实现细节。
Perhaps a better solution is to modify the code using your library
You could even return the function from the subscribe method
then if you want to store a hash or some other identifier for subscribed functions, it is opaque to the calling code meaning that you just use the returned value to unsubscribe and the library hides the implementation detail.
传入匿名函数而不是命名函数或保留稍后可用于取消订阅的引用的原因是什么?
或者,您可以允许可选的“id”参数,但这将需要不必要的复杂簿记以避免重复。
What is the reason for passing in anonymous functions rather than named ones, or keeping references that you can use for unsubscribing later?
Alternatively you could allow for an optional 'id' argument but this would require unnecessarily complex bookkeeping to avoid duplicates.