Reputation: 4984
Function.prototype
has both call
and apply
which can be used to apply a function. Today, I was going to use apply
for something, but had managed to sneak in a bit of a typo:
function foo(a) { console.log('foo', a) }
foo.call.apply(null, [1])
It took me a while to realize that I had managed to (presumably while under the influence of something) write .call
before .apply
. What surprised me however, is that this will throw a TypeError
:
> function foo(a) { console.log(a) }
undefined
> foo.call.apply(null, [1])
TypeError: object is not a function
at repl:1:10
This had me dumbfounded for a little while, until I figured that it must be because the call
function gets bound to null
. So I thought, let's try binding it to foo
and see what happens:
> foo.call.apply(foo, [1])
foo undefined
Huh, not quite what I expected. The function is clearly applied, but the argument a
is for some reason undefined
. I can't seem to figure this out. Removing the .call
obviously works as expected:
> foo.apply(foo, [1])
foo 1
Because I'm now really curious, I tried the opposite: call
the apply
function:
> foo.apply.call(foo, 1)
foo undefined
And this is about as far as I've gotten with this. I have no intention of actually using this for anything – it was an honest mistake in the first place – but I'm very curious to know what's actually going on, and was hoping one of you gurus out there might have an answer.
Why is it that call
and apply
behaves this way?
Upvotes: 4
Views: 157
Reputation: 74550
Let's start with a more informative 'foo' function:
function log() {
console.log('args: ', arguments, '\nthis: ', this);
}
All functions have the same call
and bind
methods. log.call
is a function, and as a function it has the same apply
method as every other function. So these two are equivalent:
log.apply.call === log.call
log.call === Function.prototype.call
Calling this:
log.call.apply(null, [1])
is the same as calling this:
Function.prototype.call.apply(null, [1])
The above calls the Function.prototype.call
method on window
, passing 1
as the only argument.
This is obviously very wrong; call
should only be used on functions, not the window object, and the first argument passed to call should be the execution scope, not 1
.
Upvotes: 1
Reputation: 227240
foo.call.apply(null, [1])
This calls Function.prototype.call
and sets its context to null
and passes 1
as the argument.
So, it's like you did: Function.prototype.call(1);
with the context (this
) set to null. It's trying to run the call
function of null
, so that's why you get "object is not a function".
foo.call.apply(foo, [1])
This doesn't work because of how the arguments of call
are set. You are calling Function.prototype.call(1);
with the context (this
) set to foo
. It's like you did: foo.call(1)
.
If you wanted to chain call
and apply
, you'd have to do this:
foo.call.apply(foo, [null, 1]);
Set the context of the .call
function, then set it's two parameters.
Upvotes: 3