Review my understanding of the 'this' keyword

Everybody seems to explain the ‘this’ keyword differently and many of the explanations seem to conflict with each other and all of them only partially and superficially explain it. The only unifying principle that ‘this’ is founded on seems to be the ‘execution context’ which I’m going to investigate tomorrow. For now though, this is the ‘rote’ understanding I’ve pieced together. Is the following correct?

  1. ’this’ can never be a callable object(a function or method), so it will only ever be a normal object or an array.

  2. ’this’ always refers to the default global object until you place it within the code block of a normally written and named method or function, at which point ‘this’ will search upward in scope for the first non-callable object to refer to. No matter how deeply I nest objects within objects, or arrays within arrays, or functions within functions, any references to ‘this’, unless placed within a function which is contained within and object/array, will retain the default value of the global object.

  3. within arrow functions, ‘this’ is always the default global object unless you force it to be something else.

  4. within anonymous functions, ‘this’ always refers to the default global object unless you force it to be something else via bind/apply/call or passing it from a higher scope as an argument.

  5. When in strict mode, ‘this’ is undefined unless within the code block of a named and normally written function/method or forced to be something else via bind/apply/call or passing it from a higher scope as an argument or assigning it a value from any given argument.

1 Like

I’ll answer in TLDR; format as otherwise it would be a book long.

const fn = function() {};
fn.a = 42;
fn.b = function() {
  return this.a / 2;
};

typeof fn; // "function"
fn.b(); // 21;

It think it answers your question

No

this is just a variable placeholder/name, I really doubt it can “search” for anything.

No. Arrow functions do not have concept of this therefore this will point to context in the moment of declaration as a closure.

No :slight_smile: Look example for question #1. I assign anonymous function to fn.b

No. this cannot be undefined unless you specifically choose so via call, apply or bind method

Oh boy :slight_smile: I feel like it’s time for analogies. Before we get to this let’s set up few fundamentals:

  1. Function - instruction manual, list of paper with steps BOB should do.
  2. Declaration time - time when ALICE writes manual for BOB
  3. Execution time - time when BOB works on steps written in manual

Manual consists of two parts:

  1. Scope - memory storage where variable names are stored.
  2. Body - set of instructions

Whenever BOB sees “Buy MILK” he recognized that MILK is a variable, goes to the Scope section for a lookup what MILK really is. For now we presume that he finds variable in the Scope.

Scope has 2 special variable names:

  1. ARGUMENTS
  2. THIS

Why special? Because they are getting assigned at the execution time - a bit unusual behavior for the variables. ARGUMENTS is an array of arguments passed to the function and THIS is an execution context, or simply a thing on behalf of which function is being called. So if you have function run and you call it like so run.call(rabbit) it will force rabbit to run, so BOB has ability to apply instructions from manual on other things.

There is simplified version of manual - arrow function. This manual will still have Scope, but won’t have special variables (ARGUMENTS and THIS). So what will happen if ALICE (mistakenly) will use those in these simplified manuals? BOB, at Execution time, will go to the Scope and won’t find ARGUMENTS and here starts the core of all mythification around this, because he WILL find THIS in his Scope (:exploding_head:), but THIS will have a small note that will say “Enclosed” which means it’s not an object on behalf of which function is getting executed (like with normal manuals) but rather it is a value of this enclosed here from the moment when ALICE was writing a manual. And now you need to ask me: “You just said that THIS is assigned at Execution Time, not Declaration Time, what’s the deal?”. Creators of JS decided that this will always live :slight_smile: and indeed you can use it outside functions, just open browser console and type this and it will work. So as a summary, this keyword behaves in this dualistic way depending on where it is used, just like spread/rest ... operator, plus sign + and probably other keywords. Very important thing to remember that there is no “special” mode for this, if ALICE will reference any variable from outside scope(s) it will be enclosed absolutely the same way and that’s what we call “closure” by the way :wink:

UPDATE: Just in case it’s not clear. You are ALICE and browser is BOB.
Good luck!

3 Likes

Thanks for your replies snigo. I think I’m getting there, but I have a ways to go yet because of the underlying concepts of closures and lexical scope(which are almost always omitted from the superficial explanations found all about the internet).

Here is where I’m as of now, concerning the ‘this’ keyword.

The fundamental rule of thumb

If the function making use of the ‘this’ keyword can be called in the form of object.function(), then

	'this' = object

Else

	'this' = The global object(window in browser, global in node)

Therefore,

  1. ’this’ will always have the value of the global object with nested functions which are not expressly added as methods via the member access operator. I have no idea why nested functions not added via dot notation are not treated as methods of the parent function. This arbitrary distinction was/is a major source of confusion for me regarding callable objects.

  2. ’this’ is contextual when used within an object as a method. The parent object is the execution context, otherwise, ‘this’ is the global object by default.

  3. Within arrow functions ‘this’ isn’t locally defined, so it’s inherited via the ‘enclosing lexical scope’. I don’t really understand lexical scope or closures at the moment. With arrow functions ‘this’ cannot be altered after the arrow function is declared, even with call, apply, or bind because it isn’t within the arrow function, but within the parent scope.

Hopefully, this is accurate or mostly accurate and I just have to investigate the concepts of closures and lexical scope/context to flesh it out properly.

This is roughly accurate, just be careful with words like “always” :slight_smile:
For example:

console.log(this);

Here we call log function of the console, so this has to refer to console, right? :wink:

After playing a bit with this I’ve actually realized that JS forces it to be an object-like, by running Object(value) on things you’re trying to assign to this - I’m pretty sure there is some reason behind it. So:

function a() {
  return this;
};

a(); // Window (global object);
new a(); // {}

const b = a.bind(42);
b.name; // "bound a"
b(); // Number { 42 } (an object)
b.call(window); // Number { 42 } (bound values are immutable)
new b(); // {} (new keyword ignores tightly bound this)

const c = a.bind(null);
c(); // Window (global object - this cannot be null)

const d = a.bind(undefined);
d(); // Window (same story as with null)

So my updated answer to your question #5 would be this cannot be undefined whatsoever.

If you’re trying to access this outside function it will normally point to current context - Window object in console, but also a module in Node JS module (not a global object). If we’re talking about browser’s console, the exception to this would be class, as inside the ES6 class this would refer to instance:

class This {
  print = () => console.log(this); // we're using arrow function to access outer scoped this
}

const t = new This();
t.print(); // This {}

// And to prove that THIS is enclosed:
const w = t.print;
w(); // This {}

So as you can see “always have the value of the global object” is highly inaccurate :slight_smile:

As a rule of thumb, it’s the thing (object) to the left of the dot.

When programming, there needs to be a way refer to the object that something belongs to, eg if you have a function and a property inside an object and the finction needs to access the property.

let user = {
  name: "Dan",
  sayName() {
    return this.name;
  },
}

So

user.name
    ↑
   dot

is “Dan”. That function wants to refer to the object that name belongs to, the object to the left of the dot. It can’t use user (try it, it won’t work), but it needs to refer to it. So this will refer to the object; this.name.

let user = {
  name: "Dan",
  sayName() {
    return `hello, my name is ${this.name}`
  },
  address: {
    firstLine: "1 Somewhere Street",
    postcode: "NE0 0NE",
    printAddress() {
      return `${this.firstLine}, ${this.postcode}`
    }
  }
}

So, just to make it a bit more obvious:

user.sayName()
    ↑
   dot

sayName uses this.name.
this is user.
So this.name is user.name.

user.address.printAddress()
            ↑
           dot

printAddress uses this.firstLine and this.postcode.
this is address
So this.firstLine is user.address.firstLine and this.postcode is user.address.postcode.


The global scope is another object (window in many cases but not all, as @snigo says). So it’s not doing anything different, it’s just refering to the next object up.

You can modify which object this refers to by using JS’ the functions bind, call or apply, and you can avoid it’s normal behaviour by using arrow functions, but in general, understanding it as the thing to the left of the dot works