1

I have this code

var a = function(){}
var b = a.call
b() // TypeError: b is not a function

typeof b is 'function' and console.log(b) shows ƒ call() { [native code] }.

Can someone please explain this behaviour?

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
  • 1
    Why would you want to do this? seems rather strange. – epascarello Dec 29 '17 at 14:33
  • @Andy: The OP probably thought that `a` would be automatically bound to `.call`. There are legit uses for such a bound function, when done correctly. Like if you anticipate receiving an array of items that should be the arguments of `a`, but the first array item is actually meant to be its `this` value. So if you bind `a` to `call` and store it, you can then invoke it like `boundA.apply(null, args)`, or in modern environments, `boundA(...args)`. –  Dec 29 '17 at 16:52
  • i.e. the reason to use this is ability to switch context for `a` – Andy Dec 29 '17 at 18:12
  • @epascarello because I don't want to write `Object.prototype.hasOwnProperty.call` everywhere. – Boris Verkhovskiy Oct 08 '21 at 05:24

1 Answers1

2

The reason it doesn't work is that .call() requires a function to be the this value when invoked, but you've detached it from a, so there's no more relationship there.

So because b, which is Function.prototype.call, has been given no function for a this arg, it has no valid function to invoke, so it gives an error.

If you did this, it'll work:

var b = a.call.bind(a);

That's because now the a function is bound as the this value of .call().

Another way would be to use .call to invoke .call.

var b = a.call;
b.call(a);

Now you're setting the a function to the this value of b (which again is the Function.prototype.call method), but you're doing it at the point of invocation.

  • Strange that it gives the TypeError of `b` not being a function, when `typeof b === "function"`. – Jared Smith Dec 29 '17 at 14:33
  • @JaredSmith: The message is in reference to the `this` value within `b`, since it expects a function. So right now it's getting `undefined` as the `this` value, so it's basically trying to invoke `undefined()` internally. –  Dec 29 '17 at 14:35
  • I don't think so. If one does this `var a = function(){};var b = a.call;b(function(){});` it still fails. It even fails if you do `b(a)`. – Jared Smith Dec 29 '17 at 14:38
  • @JaredSmith: That's because you're passing the function as a regular argument to `Function.prototype.call`, instead of as its `this` value. Change it to `b.call(function(){})` –  Dec 29 '17 at 14:40
  • ...Remember, when you do `a.call("foo")`, the `a` function gets automatically set as the `this` value of `.call()`, but when you do `var b = a.call`, to set the `this` value of `.call`, you need to do it manually, because the magic behavior of setting `this` via the property member syntax can no longer be achieved. –  Dec 29 '17 at 14:41
  • ...so basically `var b = a.call; b.call(function(){});` is the same as doing `Function.prototype.call.call(function(){})` –  Dec 29 '17 at 14:43
  • ...or to put it better, all these are equivalent: `a.call("foo");` ... `var b = a.call; b.call(a, "foo");` ... `Function.prototype.call.call(a, "foo");` –  Dec 29 '17 at 14:45