1

I'm reading the You Don't Know JS books and am on this and Object Prototypes.

I get that to know what this refers to I need to look at the call-site. And the call site is what this will refer to. What I don't get is why this code isn't working as I think it should (I only wrote it to understand this, not for any working problem or anything).

function foo() {
  console.log(foo.hasOwnProperty('a')); // 1
  console.log(window.hasOwnProperty('a')); // 2
  console.log(this.a); // 3
  this.a++;
  console.log(window.hasOwnProperty('a')); // 4

}
function bar(){
  foo.a = 42;
  foo();
}
bar();
  1. If you look at the first line in bar which creates an a property for foo and assigns it the value 42. If I comment this line out, then running console.log(foo.hasOwnProperty('a')); gives me false. And if I have it run, then it returns true. But if this is the case, then calling bar is indeed creating an a property for foo, right? This leads to question 3.

  2. I get that at this point window.a does not exist.

  3. Why does this return undefined? this should resolve to foo.a, right? Because the context of this would be in bar, correct? Bar is the call-site. However, this remains undefined regardless of whether foo.a = 42 is commented out or not.

  4. Why does this return true now, after running this.a++? How and why is a global variable being created?

firefiber
  • 135
  • 2
  • 10
  • 1
    The `this` keyword inside function refers to the object the function belongs to, or the window object if the function belongs to no object. So `this` and `foo` are two different objects. – Kamil Kiełczewski Mar 10 '18 at 08:34
  • `foo()` is a free function (a function that do not belongs to an object) then *this* is undefined. – Jean-Baptiste Yunès Mar 10 '18 at 08:36
  • @Jean-BaptisteYunès - simply incorrect - "this" refers to the window object. – Fraser Mar 10 '18 at 08:43
  • @fraser and what if your are in a freestanding runtime environment? ECMAScript doesn't require to have a `window` object. – Jean-Baptiste Yunès Mar 10 '18 at 08:46
  • @Jean-BaptisteYunès - But in this case there clearly is a window object, and it is part of the point of discussion. So saying "this is undefined" is simply incorrect in this case. – Fraser Mar 10 '18 at 08:49
  • Possible duplicate of [How does the "this" keyword work?](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – Fraser Mar 10 '18 at 08:50

2 Answers2

2

Note: this is just an easy way of thinking about this. In complicated cases (such as when using .bind() or OOP), the following might not be accurate.

this, by default sorta refers to the object that contains the function being called. For example, if I did

var obj = {
    fxn: function() {
        console.log(this.bar);
    },
    bar: 3
}
obj.fxn(); // (1)
var fxn = obj.fxn();
fxn(); // (2)

(1) Will print "3" to the console; Because you are, in a way, calling fxn "through" obj, this will point to obj. In other words, because you are calling the copy of fxn that is stored inside of obj, this will point to obj.

(2) Will (assuming bar has not been defined elsewhere) print undefined. That is because fxn isn't being called "through" anything. As a result, it pretends that fxn is being called through window.

Although in your example, foo is being called in bar, bar does not call a version of foo being... well, stored inside itself. bar does not call foo... "through" foo.

Admittedly, that's pretty confusing. Maybe this will clear up some confusion: If you change bar to this:

function bar(){
    foo.a = 42;
    foo.foo = foo;
    foo.foo();
}

it will print 42 instead of undefined. The important part is that the object that foo is being called through will be this. For instance,

function bar(){
    var baz = {};
    baz.a = 42;
    baz.foo = foo;
    baz.foo();
}

Will also print 42.

EKW
  • 1,859
  • 11
  • 21
  • In nearly every case, the value of "this" is determined by *how* a function is called - not what its parent is. It can't be set by assignment during execution, and it may be different each time the function is called. – Fraser Mar 10 '18 at 08:45
  • @Fraser this is also what's confusing me - isn't it a _where_ question, instead of a _how_ question? If I'm looking at _where_ the function was called, and I'm looking at the call-site, I'm looking at the where, not the how, right? – firefiber Mar 10 '18 at 08:58
  • @firefiber Wrong - it is *how* that determines the value of "this" in the function not where. That said obviously you do have to look at the callsite to see *how* it is being called. – Fraser Mar 10 '18 at 16:09
  • 1
    @Fraser I've updated my answer to hopefully articulate it better. Is this what you mean? – EKW Mar 15 '18 at 17:06
1

In foo this refers to the parent object - i.e. window.

so this.a and window.a are referring to the same thing.

Because the context of this would be in bar, correct?

Incorrect. For bar to be "this" in foo you would need to apply or call or bind foo passing your desired "this" as context. i.e.

foo.call({});

or 

foo.bind({});

Would make "this" and empty object within foo.

Why does this return true now, after running this.a++? How and why is a global variable being created?

For the same reason - when you do this.a++ you are creating and incrementing a global variable a by one.

Fraser
  • 12,565
  • 6
  • 46
  • 97
  • But why is the context **not** `bar` is what I don't get! The book says I'm to look at the call site - "The call-site we care about is in the invocation before the currently executing function." Would this make the call site `bar`? And therefore the context would be the body of the `bar` function, no? – firefiber Mar 10 '18 at 08:57
  • no - inside a function, the value of this depends on how the function is called. Because in your example the value of "this" is not set by the call, this will default to the global object - i.e. window - see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this – Fraser Mar 10 '18 at 08:59
  • 1
    I'd also point out doing `func()` and `new func()` have different meanings. As the OP has not used `new`, there is no `this` in the classic sense. – Keith Mar 10 '18 at 14:25
  • @keith - Not sure what you mean by classic sense - this is clearly defined as the global object - in every sense I can think of in the given context. – Fraser Mar 10 '18 at 14:33
  • Classic sense as in `new func()` as that's were 99% of `this` context gets bound. Ps. I'm not correcting you with anything you said, but thought worth pointing this out too, as you mentioned `apply` `bind` and `call` but just missed out `new func()`. So `this` would be global as there has been no binding whatsoever. – Keith Mar 10 '18 at 15:57
  • @Keith - Ah I get what you mean now yes...I just wasn't sure what you meant by "classic" sense...obviously strict mode and arrow functions also have nuances this too, but yes - new is a "classic" example of how this gets bound. – Fraser Mar 10 '18 at 16:02