0

I'm currently reading Chapter 2 of You Don't Know JS: this and Object Prototypes.

Let's say we have this code:

function foo() {
    console.log(this.a);
}

var obj = {
    a: 33,
    foo: foo
}

var a = 22;

I understand implicit this binding:

obj.foo(); // 33

I even understand how using it as a callback function makes it "lose" it's this value:

setTimeout(obj.foo, 1000); // undefined

What I don't understand is the following excerpt about Explicit Binding using call() and apply():

Unfortunately, explicit binding alone still doesn't offer any solution to the issue mentioned previously, of a function "losing" its intended this binding, or just having it paved over by a framework, etc.

I don't get why using call() (explicit binding) still doesn't fix this issue.

I tried using the following example to re-create how it doesn't work but it seems that setTimeout isn't able to handle using call()? It fires immediately instead of waiting 1000 ms.

setTimeout(foo.call(obj),1000);

I do realize that using setTimeout(foo.bind(obj),1000); would fix this, I'm just trying to wrap my head around understanding this excerpt from the book.

qarthandso
  • 2,100
  • 2
  • 24
  • 40

2 Answers2

8

It fires immediately instead of waiting 1000 ms

Right, because .call executes the function. Maybe this is easier to understand: foo.call(obj) is exactly the same as obj.foo(). However, setTimeout expects a function to be passed. That's why you did

setTimeout(obj.foo, 1000); 

earlier, and not

setTimeout(obj.foo(), 1000); 

So, if you can't use .call, how do you set the this value? That's what .bind solves. Instead of calling the function it creates a new function with a bound this value and this new function can then be passed around without loosing its this value.

Related: How to access the correct `this` context inside a callback?


This might not be the most precise overview but might help to understand how to relate .call/.apply and .bind to each other:

                    +-------------------+-------------------+
                    |                   |                   |
                    |      time of      |      time of      |
                    |function execution |   this binding    |
                    |                   |                   |
+-------------------+-------------------+-------------------+
|                   |                   |                   |
|  function object  |      future       |      future       |
|         f         |                   |                   |
|                   |                   |                   |
+-------------------+-------------------+-------------------+
|                   |                   |                   |
|   function call   |        now        |        now        |
|        f()        |                   |                   |
|                   |                   |                   |
+-------------------+-------------------+-------------------+
|                   |                   |                   |
|     f.call()      |        now        |        now        |
|     f.apply()     |                   |                   |
|                   |                   |                   |
+-------------------+-------------------+-------------------+
|                   |                   |                   |
|     f.bind()      |      future       |        now        |
|                   |                   |                   |
+-------------------+-------------------+-------------------+
Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thank you Felix. All of these questions are in hope of still understanding [this question](http://stackoverflow.com/questions/37368720/javascript-call-and-prototype-slice-function) that I posted a few days ago. Even with all the answers on that page and research I've done, it's still widely confusing me for some reason. – qarthandso May 27 '16 at 16:06
  • @qarthandso: If you have `foo.call(...)`, then inside `call` , `this` will refer to `foo` (because that's how `this` works, when you have `x.y()`, then inside `y`, `this` refers to `x`). Now, `call` is a function. Every function has a `bind` method. So by using `var bar = foo.call.bind(foo)`, we can bind the `this` value of `call` to the passed argument. That allows us to use `bar(...)` everywhere instead of `foo.call(...)`. Maybe my explanation helps, maybe not ;) – Felix Kling May 27 '16 at 16:21
  • I know I'm close to understanding this, and your explanations are helping, I promise! But it's weird me to think of `Function.prototype.call` being a function just like `Object.prototype.toString` is. `toString` makes sense to me, a function at the top level of the prototype chain that can be applied to the instance it's called on. But `call` to me is more like syntax of the language. It's almost like it's missing a function call before it: `Function.prototype.[some method].call`, similar to `Array.prototype.slice.call(agruments)`. – qarthandso May 27 '16 at 16:33
  • @qarthandso: I don't think my answer will help much, but you will have to "accept" the fact that `.call` is not "syntax". It's simply a function that calls another function. In ES6 you could implement it with `.apply` as `function call(thisValue, ...args) { return this.apply(thisValue, args); }`. In other words `call` simply calls its `this` value. – Felix Kling May 27 '16 at 16:50
1

The value of this changes depending on who is calling the function.

setTimeout(obj.foo, 1000); //setTimeout is the caller of foo

When you use .call it is executing the function instead of binding the this value and returning the callable function. setTimeout expects a callback function but you are feeding the result of the obj.foo. If you use an anonymous function for the setTimeout callback, you should see the expected result:

setTimeout(function(){
 foo.call(obj);
}, 1000);