Function.prototype.bind

Garrett Smith dhtmlkitchen at gmail.com
Wed Sep 10 11:04:06 PDT 2008


On Tue, Sep 9, 2008 at 9:47 PM, David-Sarah Hopwood
<david.hopwood at industrial-designers.co.uk> wrote:
> Mark S. Miller wrote:
>> On Tue, Sep 9, 2008 at 9:21 AM, Mark S. Miller <erights at google.com> wrote:
>>> I still need to spec Function.prototype.bind in our funny spec language.
>>>    Anyone care to contribute a draft?


>
> I have also assumed the existence of a "static" Function.apply which
> takes the function to be applied as an explicit first argument. This
> is necessary because otherwise there is no way to bind 'this' correctly
> given that thisFunc.apply may have been overridden (or at least, I
> don't know of any way to do it).
>

That code cannot be tested it in any implementations that do not have
your Function.apply.
if(!Function.apply) {
  ...
}

Just for fun...

You'll have to wait a few 10 seconds to see any results. The returned
function is faster when it does bind only (GarrettsBind tests #3 and
#4, no partialApply), or a partialApply called with 0 args
(GarrettsBind test #1).

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
  <meta http-equiv="content-type"
    content="text/html; charset=iso-8859-1">
  <title>BindFunction.html</title>
</head>
<body>
<script type="text/javascript">
/**
 * @param {Object} context - the - this - value to be used.
 * @param {arguments} [1..n] optional arguments that are
 * prepended to returned function's call.
 * @return {Function} a function that applies the original
 * function with - context - as the thisArg.
 */

var bindFunctions = [
  {
    name : "GarrettsBind",
    fun:function GarrettsBind(context){
      var fn = this,
          slice = Array.prototype.slice,
          args,
          isPartial = arguments.length > 1;
          if(isPartial)
            args = slice.call(arguments, 1);

      // Strategy 1: just bind, not a partialApply
      if(!isPartial) {
        return function() {
          if(arguments.length !== 0) {
            return fn.apply(context, arguments);
          } else {
            return fn.call(context); // faster in Firefox.
          }
        };
      } else {
      // Strategy 2: partialApply
        return function() {
            return fn.apply(context,
              arguments.length === 0 ? args :
                args.concat(slice.call(arguments)));
        };
      }
    }
  },
  {
    name : "MarksBind",
    fun : function MarksBind(self, var_args) {
      var thisFunc = this;
      var slice = Array.prototype.slice;
      var leftArgs = slice.call(arguments, 1);
      return function(var_args) {
        var args = leftArgs.concat(slice.call(arguments, 0));
        return thisFunc.apply(self, args);
      };
    }
  }
];
var currBindFunction = 0;

// Mock Data
function x(a,b,c) {
  return ((a||0) + (b||0) + (c||0) + this.v);
}
var o = {v: 1000};
var value;
var ITER = 10000;

var d = new Date,
    result,
    resultsDiv,
    currentTest = 0,
    results = [bindFunctions[0].name];

function oncomplete() {
  if(++currentTest === tests.length) {
    if(++currBindFunction === bindFunctions.length) {
      resultsDiv = document.getElementById('resultsDiv');
      resultsDiv.innerHTML =
        results.join('\n---------------------\n');
      return;
    } else {
      var bo = bindFunctions[currBindFunction];
      currentTest=0;
      results.push('\n', bo.name);
      Function.prototype.bind = bo.fun;
    }
  }
  setTimeout(function(){tests[currentTest].run();}, 500);
}

var tests = [
  {
    run : (function testBindPartialNoArgs() {
      d=new Date;
      var adder = x.bind(o, 1, 10, 100);
      for(var i = 0; i < ITER; i++) {
        result = adder();
      }
      results.push(
        new Result("x.bind(o, 1, 10, 100); adder()",
        result===1111, new Date-d)
      );
      oncomplete();
    })
  },

  {
    run : (function testBindPartialWithArgs() {
      d=new Date;
      var adder = x.bind(o, 1);
      for(var i = 0; i < ITER; i++) {
        result = adder(10, 100);
      }
      results.push(
        new Result("x.bind(o, 1); adder(10, 100)",
        result===1111, new Date-d)
      );
      oncomplete();
    })
  },

  {
    run : (function testBindWithArgs() {
      d=new Date;
      var adder = x.bind(o);
      for(var i = 0; i < ITER; i++) {
        result = adder(1, 10, 100);
      }
      results.push(
        new Result("x.bind(o); adder(1, 10, 100)",
        result===1111, new Date-d)
      );
      oncomplete();
    })
  },
  {
    run : (function testBindNoArgs() {
      d=new Date;
      var adder = x.bind(o);
      for(var i = 0; i < ITER; i++) {
        result = adder();
      }
      results.push(
        new Result("x.bind(o); adder()",
        result===1000, new Date-d)
      );
      oncomplete();
    })
  }
];

Function.prototype.bind = bindFunctions[0].fun;
tests[0].run();
tests
function Result(s, pass, time) {
  this.s = s;
  this.result = (pass === true ? "PASS" : "FAIL");
  this.time = time;
}
Result.prototype.toString = function() {
  return this.s +
    "\nresult: " + this.result
    + "\ntime: " + this.time;
}

</script>
<pre id="resultsDiv"></pre>
</body>
</html>


(To be able to test in other browsers, Mark's bind was modified so
that it does not use Array.slice, but instead
Array.prototype.slice.call)

Garrett
> --
> David-Sarah Hopwood


More information about the Es-discuss mailing list