Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the meaning to chain call and apply together?

I come across this code in jsGarden, and I cannot figure the meaning to chain call and apply together. Both will execute the function with a given context object, why it could be chained?

function Foo() {}

Foo.prototype.method = function(a, b, c) {
    console.log(this, a, b, c);
};

// Create an unbound version of "method" 
// It takes the parameters: this, arg1, arg2...argN
Foo.method = function() {

    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
    Function.call.apply(Foo.prototype.method, arguments);
};
like image 654
steveyang Avatar asked Jul 26 '12 16:07

steveyang


2 Answers

It's making a call to call via apply; that is, it's using call to call a function ("method"), and it's using apply to make the call because it's got the arguments in the form of an (almost) array.

So to take it apart:

Function.call

That's a reference to the call() function available on all Function instances, inherited from the Function prototype.

Function.call.apply

That's a reference, via the reference to the call function, to apply. Because apply is referenced via the call object, when the call to apply is made the this value will be a reference to the call function.

Function.call.apply(Foo.prototype.method, arguments);

So we're invoking the call function via apply, and passing Foo.prototype.method to be the this value, and the arguments to "Foo.mmethod" as the arguments.

I think it's basically the same effect as this:

Foo.method = function() {
  var obj = arguments[0], args = [].slice.call(arguments, 1);
  Foo.prototype.method.apply(obj, args);
}

but I'll have to try it to make sure. edit Yes that seems to be it. So I can summarize the point of that trick as being a way to invoke apply() when the desired this value is the first element of the array holding the parameters. In other words, usually when you call apply() you've got the desired this object reference, and you've got the parameters (in an array). Here, however, since the idea is that you pass in the desired this as a parameter, then it needs to be separated out in order for a call to apply to be made. Personally I would do it as in my "translation" because it's a little less mind-bending (to me), but I suppose one could get used to it. Not a common situation, in my experience.

like image 161
Pointy Avatar answered Nov 12 '22 02:11

Pointy


I think the code should be like this:

function Foo() {}

Foo.prototype.method = function(a, b, c) {
 console.log(this, a, b, c);
};

Foo.method = function() {

 //Notice this line:
 Function.apply.call(Foo.prototype.method, this, arguments);
};

then

Foo.method(1,2,3) => function Foo() {} 1 2 3

Other examples:

Function.apply.call(Array,this,[1,2]) => [1, 2]
Function.call.apply(Array,this,[1,2]) => [window]
Function.call.call(Array,this,[1,2])  => [[1, 2]]
like image 27
foxiris Avatar answered Nov 12 '22 04:11

foxiris