Marcus Stade
Marcus Stade

Reputation: 4984

Why does applying the call function not pass the correct argument?

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

Answers (2)

Web_Designer
Web_Designer

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

gen_Eric
gen_Eric

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

Related Questions