bind vs emulate (v7)

Revision 7 of this benchmark created on


Preparation HTML

<script src="//www.cinsoft.net/mylib099-min.js"></script>

Setup

Function.prototype.bindRaynos = function (context) {
      var f = this;
      var curriedArgs = Array.prototype.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 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 };
    
    var f = function () {
      this.i++;
    };
    
    var nativelyBound = f.bind(i);
    var raynosBound = f.bindRaynos(i);
    var es5shimBound = f.bindEs5Shim(i);

Test runner

Ready to run.

Testing in
TestOps/sec
Native version
nativelyBound();
ready
Raynos's version
raynosBound();
ready
es5-shim version
es5shimBound();
ready

Revisions

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