Why is lodash.each faster than native forEach?

前端 未结 4 1985
别跟我提以往
别跟我提以往 2020-12-04 14:16

I was trying to find the fastest way of running a for loop with its own scope. The three methods I compared were:

var a = \"t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t         


        
相关标签:
4条回答
  • 2020-12-04 14:26

    Yes, lodash/underscore each don't even have same semantics as .forEach. There is a subtle detail that will make the function really slow unless the engine can check for sparse arrays without getters quickly.

    This will be 99% spec compliant and runs at the same speed as lodash each in V8 for the common case:

    function FastAlmostSpecForEach( fn, ctx ) {
        "use strict";
        if( arguments.length > 1 ) return slowCaseForEach();
        if( typeof this !== "object" ) return slowCaseForEach();
        if( this === null ) throw new Error("this is null or not defined");
        if( typeof fn !== "function" ) throw new Error("is not a function");
        var len = this.length;
        if( ( len >>> 0 ) !== len ) return slowCaseForEach();
    
    
        for( var i = 0; i < len; ++i ) {
            var item = this[i];
            //Semantics are not exactly the same,
            //Fully spec compliant will not invoke getters
           //but this will.. however that is an insane edge case
            if( item === void 0 && !(i in this) ) {
                continue;
            }
            fn( item, i, this );
        }
    }
    
    Array.prototype.fastSpecForEach = FastAlmostSpecForEach;
    

    By checking for undefined first, we don't punish normal arrays in the loop at all. An engine could use its internals to detect strange arrays but V8 doesn't.

    0 讨论(0)
  • 2020-12-04 14:31

    Here's an updated link (circa 2015) showing the performance difference which compares all three, for(...), Array.forEach and _.each: https://jsperf.com/native-vs-underscore-vs-lodash

    Note: Put here since I didn't have enough points yet to comment on the accepted answer.

    0 讨论(0)
  • 2020-12-04 14:39

    _.each() is not fully compatible to [].forEach(). See the following example:

    var a = ['a0'];
    a[3] = 'a3';
    _.each(a, console.log); // runs 4 times
    a.forEach(console.log); // runs twice -- that's just how [].forEach() is specified
    

    http://jsfiddle.net/BhrT3/

    So lodash's implementation is missing an if (... in ...) check, which might explain the performance difference.


    As noted in the comments above, the difference to native for is mainly caused by the additional function lookup in your test. Use this version to get more accurate results:

    for (var ix = 0, len = a.length; ix < len; ix++) {
      cb(a[ix]);
    }
    

    http://jsperf.com/lo-dash-each-vs-native-foreach/15

    0 讨论(0)
  • 2020-12-04 14:47

    http://kitcambridge.be/blog/say-hello-to-lo-dash/

    The lo-dash developers explain (here and on a video) that the relative speed of the native forEach varies among browsers. Just because forEach is native does not mean that it is faster than a simple loop built with for or while. For one thing, the forEach has to deal with more special cases. Secondly, forEach uses callbacks, with the (potential) overhead of function invocation etc.

    chrome in particular is known (at least to the lo-dash developers) to have a relatively slow forEach. So for that browser, lo-dash uses it's own simple while loop to gain speed. Hence the speed advantage that you see (but others don't).

    By smartly opting into native methods — only using a native implementation if it’s known to be fast in a given environment — Lo-Dash avoids the performance cost and consistency issues associated with natives.

    0 讨论(0)
提交回复
热议问题