开发者

Resolve two anonymous functions to the same definition point?

I have code in the following form in which the user may specify a callback which will be called at a later time:

var _deferred = [];

var deferred = function(callback) {
    _deferred.push(callback);
}

var doDeferred = function() {
    for(var i = 0, max = _deferred.length; i < max; i++) {
        _deferred[i].call();
    }
}

for(var i = 0; i < 5; i++) {
    deferred(function() {
        console.log("Some deferred stuff");
    });
}

doDeferred();

I would like to recognize that the callback specified to 开发者_StackOverflow中文版deferred() is an anonymous function resolving to the same origin, and only allow it to be added once. Aka in the bottom for loop it would throw an exception when i = 1.

Like:

var deferred = function(callback) {
        if(_deferred.indexOf(callback) !== -1) {
                throw "Already added!";
        }
        _deferred.push(callback);
}

I can think of many ways of doing this by adding a "key", but I'm wondering if I can use something along the lines of Function.caller to create a "source hash"?

Is there a solution for this out there already that I'm not seeing? I'd really prefer to accept this burden onto myself rather than push it out to the caller of deferred and have them provide some sort of unique id.

EDIT:

To clarify any confusion.

Yes each call to deferred is with a unique function object, has its own closure, etc. Therefore my indexOf will always fail. That is not a point of confusion.

The question is that these anonymous functions are declared in the same place, they are the same code, how can I determine that? I'm looking to determine declarative equality, not instance equality. I was thinking I could somehow create a hash based on caller of deferred...

CONCLUSION:

Thanks guys, it seems like there is no really elegant solution here. The toString method is not definitive (different functions with same body will test equally as strings) - and is just ugly. I'm going to put the burden on the caller.


The thing is, in the loop at the bottom, they are different functions, so in all fairness they should both be included (and honestly, there is no guarantee that the values from both functions won't be different depending on the variables present at the moment). I'm also not sure that 'unique functions only' is something which people expect, so it might cause a good deal of "debugging"

This isn't something which is required of ECMAScript, but Function.toString() will generally return its internal structure. So you probably want:

var ids = [] // separate ID takes up slightly more space, but lookup should 
             // be faster.
var deferred = function(callback) {
        var cbs = callback.toString() // or String(callback)
        if(ids.indexOf( cbs ) !== -1)
        {
                throw "Already added!";
        }
        ids.push( cbs )
        _deferred.push(callback);
}

If you're willing to use a for... in loop:

var _deferred = {}
var deferred = function(callback) {
        var cbs = callback.toString() // or String(callback)
        if( _deferred[ cbs] )
        {
                throw "Already added!";
        }
        _deferred[ cbs] = callback;
}

// caution, this executes in arbitrary order.
var doDeferred = function() {
    for(var i in _deferred) {
        _deferred[i].call();
    }
}


As far as I know, if you have two anonymous functions with the same bodies, they will not be equivalent in terms of the "==" operator, so I don't think you can do the indexOf().

You could check if your new function has the same body as any function already in your array. To do this just convert the function to a string and check for equality:

String((function(){/*something*/})) == String((function(){/*something*/}))

Should return true;


var deferred = function(callback) {
    if(_deferred.some(function(c){return c.toString() === callback.toString()})) {
            throw "Already added!";
    }
    _deferred.push(callback);
}

This will throw the error the first time a duplicate method signature is added.


Though I wouldn't consider this elegant, you can compare anonymous functions by using their toString() method.

var deferred = function(callback) {
        var str = callback.toString();
        for var i = 0; i < _deferred.length; i++) {
            if (_deferred[i].toString() == str) {
                throw "Already added!";
            }
        }
        _deferred.push(callback);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜