Question

From: http://ejohn.org/apps/learn/#2

Function.prototype.bind = function(){
  var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
  return function(){
    return fn.apply(object,
      args.concat(Array.prototype.slice.call(arguments)));
  };
};

Can anyone tell me why the second return is necessary (before fn.apply)?

Also, can anyone explain why args.concat is necessary? Why wouldn't it be re-written as:

fn.apply(object, args)

instead of

return fn.apply(object,
          args.concat(Array.prototype.slice.call(arguments)));
Was it helpful?

Solution

The second return is necessary because otherwise we will lose any return value from the bound function.

You may already know this, but doesn't hurt to mention. If we don't wrap fn.apply inside another function, then we are directly calling the function fn which is sub-optimal, as bind is only supposed to set the execution context (what should this refer to inside the function), and not invoke it.

Javascript methods can be invoked by calling the call or apply method on them. Here's a small example:

function example() {
    alert("useless example");
}

example.apply() // or example.call(), alerts "useless example";

The outer function in Prototype's bind() is supposed to work like an invisible wrapper around the bound function. Thus any arguments that are passed to the wrapper should be passed to the bound function as well, and it must return any value that the bound function returns, which is why the return statement is there.

The reason for doing args.concat inside fn.apply is different and it's not optional. bind in Prototype allows you to prepend arguments to the bound function.

args represents the arguments that were passed when we called bind on the function. arguments represents the arguments that were passed when we called the bound function. We're basically concatenating two arrays there.

From the above example:

var obj = { x: 'prop x' };
var boundExample = example.bind(obj, 12, 23); // args => 12, 23
boundExample(36, 49); // arguments => 36, 49

// arguments that our example() function receives => 12, 23, 36, 49

OTHER TIPS

Old post but a newer approach ;)

Function.prototype.bind = function(){
    var fn = this, 
    context = arguments[0], 
    args = Array.prototype.slice.call(arguments, 1);
    return function(){
        return fn.apply(context, args.concat([].slice.call(arguments)));
    }
}

obj = {'abc':'x'};
var func = function() {
  console.log(arguments);
  alert(this.abc);
}
var x = func.bind(obj);

console.log(x(1,2,3));

This is a very good example to explain. Run it and check the console log. Then modify the code to leave out

[].slice.call(arguments)

You will see that the console.log of the execution of x(1,2,3) is not showing up the arguments anymore. This is because the arguments objects is a local variable within all functions. That might sound a bit confusing, but what it means is basically:

var x = func.bind(obj,1,2,3);

Returns this function internally:

function() {
    return fn.apply(obj, [1,2,3].concat([].slice.call(arguments)));
}

So it is more of a template for the function.

When you do now run it like:

x(4,5,6)

This will be ran:

fn.apply(obj, [1,2,3].concat([].slice.call(arguments)))

with a special arguments object = {0:4,1:5,2:6} , which can be converted into an array using [].slice.call where arguments is a local object, that is automatically beeing assigned during the invokation of the function.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top