How optimizeCb in underscorejs work?

Viewed 474

I've been contemplating this, from underscorejs:

  var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
    switch (argCount == null ? 3 : argCount) {
      case 1: return function(value) {
        return func.call(context, value);
      };
      case 2: return function(value, other) {
        return func.call(context, value, other);
      };
      case 3: return function(value, index, collection) {
        return func.call(context, value, index, collection);
      };
      case 4: return function(accumulator, value, index, collection) {
        return func.call(context, accumulator, value, index, collection);
      };
    }
    return function() {
      return func.apply(context, arguments);
    };
  };

So, apparently this optimization applies only to callback that sets this value (here called context). How is this different from directly call call to the callback? How this is able to improve performance?

It's ok if the optimization is only valid for legacy JS engines. I'm just dying to know.

Edit

I was probably unclear in the question. This is what I mean. Let's take an example where optimizeCb is used:

  _.each = _.forEach = function(obj, iteratee, context) {
    iteratee = optimizeCb(iteratee, context); //REMOVE this
    var i, length;
    if (isArrayLike(obj)) {
      for (i = 0, length = obj.length; i < length; i++) {
        iteratee(obj[i], i, obj);
        //REPLACE with iteratee.call(context, obj[i], i, obj);
      }
    } else {
      var keys = _.keys(obj);
      for (i = 0, length = keys.length; i < length; i++) {
        iteratee(obj[keys[i]], keys[i], obj);
      }
    }
    return obj;
  };

See the 2 comments: iteratee = optimizeCb(iteratee, context); //REMOVE this and iteratee(obj[i], i, obj); //REPLACE with iteratee.call(context, obj[i], i, obj);. I understand arguments is slow and apply is slow. But I don't see how arguments and call vs apply come into play here? I see no difference between 2 approaches.

I think the key problem is that if a callback is passed to a certain underscore method, then the signature is already known. For example, callback passed into _.each has to have function(value, index, collection). This observation is confirmed in the way optimizeCb is called: if caller of optimizeCb is able to provide the argCount parameter (leave blank means it's 3), it knows which signature it is.

Can someone further elaborate? Many thanks!

1 Answers
Related