Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

_.any can return incorrect results when native forEach functions are used #177

Closed
lshearer opened this issue Apr 21, 2011 · 9 comments
Closed

Comments

@lshearer
Copy link

When a native forEach function is present, if _.any is called with a list in which the last item does not pass the truth test it will always return false (even if any other items in the list passed the truth test). The problem is that the native forEach function does not short circuit (return breaker; has no effect on it). Therefore, the return result for the call to _.any will be overwritten by the last item.

Suggested fix:

Replace

            if (result = iterator.call(context, value, index, list)) return breaker;

with

            if (result |= iterator.call(context, value, index, list)) return breaker;

in _.any, resulting in:

    var any = _.some = _.any = function (obj, iterator, context) {
        iterator = iterator || _.identity;
        var result = false;
        if (obj == null) return result;
        if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
        each(obj, function (value, index, list) {
            if (result |= iterator.call(context, value, index, list)) return breaker;
        });
        return !!result;
    };

This prevents result from being set to true and then later being overwritten by false.

Edit: because the |= operation returns an integer, to maintain a strictly boolean return type, the return value would need to be !!result

@shinuza
Copy link
Contributor

shinuza commented May 7, 2011

The is issue exists but the fix is partially incorrect, the original code reads :

if (result = iterator.call(context, value, index, list)) return breaker;

not

if (result == iterator.call(context, value, index, list)) return breaker;

which does override result with whatever the iterator returns if the each doesn't support iteration break.
See pull request #197 for the fix.

@lshearer
Copy link
Author

lshearer commented May 7, 2011

Yes, I mistyped the original code, but my fix is correct. If result is ever set to true, the |= operation will never set it back to false. Your fix also achieves the correct result, but I did it this way because it is slightly more compact.

@shinuza
Copy link
Contributor

shinuza commented May 7, 2011

Sorry I misread |= as !=, that's why I thought something was wrong with your code. Pull request updated.

@jashkenas
Copy link
Owner

Already commented this on the pull request -- but this needs to have some test cases added.

@jashkenas
Copy link
Owner

This should be fixed as of the corresponding pull request.

@jdalton
Copy link
Contributor

jdalton commented Aug 3, 2011

I dig this patch/issue... I'm curious under what circumstances did this bug happen (an object passed the has native forEach(), but not some())?

@shinuza
Copy link
Contributor

shinuza commented Aug 3, 2011

Would happen if your environment doesn't support neither forEach nor some, and you add forEach manually by extending Array.prototype, then use .any/.some.

@chrisaaaker
Copy link

Repeating the question from @jdalton ( but modified ), from 2 years ago, are there any cases when a native forEach is present but a native some is not present, AND the prototype chain has not been user modified to effect this? That is, a browser has implemented the language this way?

@jdalton
Copy link
Contributor

jdalton commented Jun 28, 2013

@chrisaaaker It's for when a 3rd-party lib shims one native array method and not another.

hongmaoxiao pushed a commit to hongmaoxiao/underscore_source that referenced this issue Oct 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants