bind vs emulate

JavaScript performance comparison

Revision 10 of this test case created by termi

Preparation code

<script src="//www.cinsoft.net/mylib099-min.js"></script>
 
<script>
Benchmark.prototype.setup = function() {
    Function.prototype.bindRaynos = function (context) {
      var f = this;
      var curriedArgs = slice.call(arguments, 2);
      if (curriedArgs.length) {
        return function () {
          var allArgs = curriedArgs.slice(0);
          for (var i = 0, n = arguments.length; i < n; ++i) {
            allArgs.push(arguments[i]);
          }
          f.apply(context, allArgs);
        };
      } else {
        return createProxy(f, context);
      }
    };
   
    Function.prototype.bindRaynos_concat = function (context) {
      var f = this;
      var curriedArgs = slice.call(arguments, 2);
      if (curriedArgs.length) {
        return function () {
          f.apply(context, allArgs.concat(slice.call(arguments)));
        };
      } else {
        return createProxy(f, context);
      }
    };
   
    function createProxy(f, context) {
      return function bound() {
        if (!(this instanceof bound)) {
          f.apply(context, arguments);
        }
      }
    }
   
    var slice = Array.prototype.slice;
   
    Function.prototype.bindEs5Shim = function bind(that) { // .length is 1
        // 1. Let Target be the this value.
        var target = this;
        // 2. If IsCallable(Target) is false, throw a TypeError exception.
        if (typeof target != "function") {
            throw new TypeError(); // TODO message
        }
        // 3. Let A be a new (possibly empty) internal list of all of the
        // argument values provided after thisArg (arg1, arg2 etc), in order.
        // XXX slicedArgs will stand in for "A" if used
        var args = slice.call(arguments, 1); // for normal call
        // 4. Let F be a new native ECMAScript object.
        // 11. Set the [[Prototype]] internal property of F to the standard
        // built-in Function prototype object as specified in 15.3.3.1.
        // 12. Set the [[Call]] internal property of F as described in
        // 15.3.4.5.1.
        // 13. Set the [[Construct]] internal property of F as described in
        // 15.3.4.5.2.
        // 14. Set the [[HasInstance]] internal property of F as described in
        // 15.3.4.5.3.
        var bound = function () {
   
            if (this instanceof bound) {
                // 15.3.4.5.2 [[Construct]]
                // When the [[Construct]] internal method of a function object,
                // F that was created using the bind function is called with a
                // list of arguments ExtraArgs, the following steps are taken:
                // 1. Let target be the value of F's [[TargetFunction]]
                // internal property.
                // 2. If target has no [[Construct]] internal method, a
                // TypeError exception is thrown.
                // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
                // property.
                // 4. Let args be a new list containing the same values as the
                // list boundArgs in the same order followed by the same
                // values as the list ExtraArgs in the same order.
                // 5. Return the result of calling the [[Construct]] internal
                // method of target providing args as the arguments.
   
                var F = function(){};
                F.prototype = target.prototype;
                var self = new F;
   
                var result = target.apply(
                    self,
                    args.concat(slice.call(arguments))
                );
                if (Object(result) === result) {
                    return result;
                }
                return self;
   
            } else {
                // 15.3.4.5.1 [[Call]]
                // When the [[Call]] internal method of a function object, F,
                // which was created using the bind function is called with a
                // this value and a list of arguments ExtraArgs, the following
                // steps are taken:
                // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
                // property.
                // 2. Let boundThis be the value of F's [[BoundThis]] internal
                // property.
                // 3. Let target be the value of F's [[TargetFunction]] internal
                // property.
                // 4. Let args be a new list containing the same values as the
                // list boundArgs in the same order followed by the same
                // values as the list ExtraArgs in the same order.
                // 5. Return the result of calling the [[Call]] internal method
                // of target providing boundThis as the this value and
                // providing args as the arguments.
   
                // equiv: target.call(this, ...boundArgs, ...args)
                return target.apply(
                    that,
                    args.concat(slice.call(arguments))
                );
   
            }
   
        };
        // XXX bound.length is never writable, so don't even try
        //
        // 15. If the [[Class]] internal property of Target is "Function", then
        // a. Let L be the length property of Target minus the length of A.
        // b. Set the length own property of F to either 0 or L, whichever is
        // larger.
        // 16. Else set the length own property of F to 0.
        // 17. Set the attributes of the length own property of F to the values
        // specified in 15.3.5.1.
   
        // TODO
        // 18. Set the [[Extensible]] internal property of F to true.
   
        // TODO
        // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
        // 20. Call the [[DefineOwnProperty]] internal method of F with
        // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
        // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
        // false.
        // 21. Call the [[DefineOwnProperty]] internal method of F with
        // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
        // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
        // and false.
   
        // TODO
        // NOTE Function objects created using Function.prototype.bind do not
        // have a prototype property or the [[Code]], [[FormalParameters]], and
        // [[Scope]] internal properties.
        // XXX can't delete prototype in pure-js.
   
        // 22. Return F.
        return bound;
    };
   
    var i = { i: 0 };
   
    i.f = function() {
      this.i++;
    };
   
    var f = function (a) {
      this.i += a;
    };
   
    var nativelyBound_1 = f.bind(i, 1);
    var raynosBound_1 = f.bindRaynos(i, 1);
    var es5shimBound_1 = f.bindEs5Shim(i, 1);
    var raynosBound_concat_1 = f.bindRaynos_concat(i, 1);
   
    var nativelyBound = f.bind(i);
    var raynosBound = f.bindRaynos(i);
    var es5shimBound = f.bindEs5Shim(i);
    var raynosBound_concat = f.bindRaynos_concat(i);
};
</script>

Preparation code output

Test runner

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

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Native version with params
nativelyBound_1();
pending…
Raynos's version with params
raynosBound_1();
pending…
es5-shim version with params
es5shimBound_1();
pending…
Raynos's version + concat with params
raynosBound_concat_1();
pending…
Native version
nativelyBound();
pending…
Raynos's version
raynosBound();
pending…
es5-shim version
es5shimBound();
pending…
Raynos's version + concat
raynosBound_concat();
pending…
i.f
i.f(1)
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. Here’s a list of current revisions for this page:

0 comments

Add a comment