Function Overloading

JavaScript performance comparison

Test case created by Unkn0wn

Preparation code

<script>
  function Dimension() {}

  function Point() {}

  var Util = {};
  // \fn Redirect(args, func, ...)
  // Will call the given function if the args (arguments) match the
  // function signature determined by the variable arguments after the func
  // parameter. Example:
  //  Util.Redirect.apply(this, [arguments, DoFunction, 'number', Point, 'number']).
  // or:
  //  Util.Redirect(arguments, func, typeof(0), Point, typeof(0));
  //
  // The example above will call DoFunction(arguments), passing the this pointer
  // if args matches the ('number', Point, 'number') signature.
  //  \returns null on failure, otherwise the it returns the result inside of an
  //    array (this allows redirected functions returning null to not be confused
  //    with a failed redirect).
  Util.Redirect = function(args, func) {

    'use strict';
    // Count of arguments for this function before varargs.
    var REDIRECT_ARGUMENT_COUNT = 2;

    if (arguments.length - REDIRECT_ARGUMENT_COUNT !== args.length) {
      return null;
    }

    for (var i = REDIRECT_ARGUMENT_COUNT; i < arguments.length; ++i) {
      var argsIndex = i - REDIRECT_ARGUMENT_COUNT;

      var currentArgument = args[argsIndex];
      var currentType = arguments[i];

      // Adjust the type to compare with
      if (typeof(currentType) === 'object') {
        // Turn currentType from object to function.
        currentType = currentType.constructor;
      }

      if (typeof(currentType) === 'number') {
        currentType = 'number';
      }

      if (typeof(currentType) === 'string' && currentType === '') {
        currentType = 'string';
      }

      // Test current argument against type.
      if (typeof(currentType) === 'function') {
        if (!(currentArgument instanceof currentType)) {
          // Fail !!
          return null;
        }
      } else {
        // Argument is a primitive (and represented as a string) ...
        if (typeof(currentArgument) !== currentType) {
          // Fail !!
          return null;
        }
      }

    }

    // All tests have passed.
    return [func.apply(this, args)];

  }


  function FuncPoint(point) {}

  function FuncPointDispatch(point) {
    FuncPoint.apply(this, arguments);
  }

  function FuncDimension(dimension) {}

  function FuncDimensionDispatch(dimension) {
    FuncDimension.apply(this, arguments);
  }

  function FuncDimensionPoint(dimension, point) {}

  function FuncDimensionPointDispatch(dimension, point) {
    FuncDimensionPoint.apply(this, arguments);
  }

  function FuncXYWidthHeight(x, y, width, height) {}

  function FuncXYWidthHeightDispatch(x, y, width, height) {
    FuncXYWidthHeight.apply(this, arguments);
  }

  function Func() {

    'use strict';
    Util.Redirect(arguments, FuncPoint, Point);
    Util.Redirect(arguments, FuncDimension, Dimension);
    Util.Redirect(arguments, FuncDimensionPoint, Dimension, Point);
    Util.Redirect(arguments, FuncXYWidthHeight, 0, 0, 0, 0);
  }

  function FuncArgTestProperty(arg) {

    'use strict';
    var hasPoint = arg.hasOwnProperty('point'),
        hasDimension = arg.hasOwnProperty('dimension'),
        hasX = arg.hasOwnProperty('x'),
        hasY = arg.hasOwnProperty('y'),
        hasWidth = arg.hasOwnProperty('width'),
        hasHeight = arg.hasOwnProperty('height'),
        hasXYWidthHeight = hasX & hasY & hasWidth & hasHeight;

    if (hasPoint & !hasDimension) {
      FuncPoint.apply(this, [arg.point]);
    } else if (!hasPoint & hasDimension) {
      FuncDimension.apply(this, [arg.dimension]);
    } else if (hasPoint & hasDimension) {
      FuncDimensionPoint.apply(this, [arg.dimension, arg.point]);
    } else if (hasXYWidthHeight) {
      FuncXYWidthHeight.apply(this, [arg.x, arg.y, arg.width, arg.height]);
    }
  }

  function FuncInTest(arg) {

    'use strict';
    var hasPoint = 'point' in arg,
        hasDimension = 'dimension' in arg,
        hasX = 'x' in arg,
        hasY = 'y' in arg,
        hasWidth = 'width' in arg,
        hasHeight = 'height' in arg,
        hasXYWidthHeight = hasX & hasY & hasWidth & hasHeight;

    if (hasPoint & !hasDimension) {
      FuncPoint.apply(this, [arg.point]);
    } else if (!hasPoint & hasDimension) {
      FuncDimension.apply(this, [arg.dimension]);
    } else if (hasPoint & hasDimension) {
      FuncDimensionPoint.apply(this, [arg.dimension, arg.point]);
    } else if (hasXYWidthHeight) {
      FuncXYWidthHeight.apply(this, [arg.x, arg.y, arg.width, arg.height]);
    }
  }
</script>
    

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Function overloading
Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point);
Func(0, 0, 0, 0);
pending…
Direct method calling
FuncPoint(new Point());
FuncDimension(new Dimension());
FuncDimensionPoint(new Dimension(), new Point());
FuncXYWidthHeight(0, 0, 0, 0);
pending…
Passing object, test hasOwnProperty
FuncArgTestProperty(new Point());
FuncArgTestProperty(new Dimension());
FuncArgTestProperty(new Dimension(), new Point());
FuncArgTestProperty(0, 0, 0, 0);
pending…
Passing object, test 'in' object
FuncInTest({
  point: new Point()
});
FuncInTest({
  dimension: new Dimension()
});
FuncInTest({
  dimension: new Dimension(),
  point: new Point()
});
FuncInTest({
  x: 0,
  y: 0,
  width: 0,
  height: 0
});
pending…
Direct method calling +apply
// Func*Dispatch just calls Func*.apply(this, arguments).
FuncPointDispatch(new Point());
FuncDimensionDispatch(new Dimension());
FuncDimensionPointDispatch(new Dimension(), new Point());
FuncXYWidthHeightDispatch(0, 0, 0, 0);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments