A story about how `this` works
Tags: react, javascript
Published: 2023-09-04 03:33:55

## `this` in the global context
this works differently everywhere. Let's start with this in global context. The funny thing is, this refuses to work similarly even in the global context depending on the running environment. And even there, it's just behaves crazy sometimes.
Let's start with this in the browser. 
```js
console.log(this === window);
```
Writing this in the window console will give you true showing that the this in the global context is the window object.
Now let's see what happens when you run it in Node JS.
Let's take the REPL and type
```js
this === global
```
this will give true again which shows that the global object in Node JS is the this keyword.
So what happens if I write the above line in an an index.js file and run it with Node JS? That gives false, hmmm, crazy Javascript.
This is because in the index.js file the this keyword points to module.exports. So you can do something like 
```js
console.log(this === module.exports)
```
to get true.
## this in functions
Now let's see what happens in functions.
In an ordinary function
```js
function fn() {
    console.log(this === global);
}

fn();
```
but if it is in strict mode,
```js
"use strict"

function fn() {
    console.log(this === undefined);
}

fn();
```
Even if just the function is in strict mode then the `this `will be set to `undefined`
## this in closures and some crazy stuff
This part if where I am hoping you will figure out how this works in a better sense.
Let's start with writing a function to throttle.
```js
function throttle(fn, limit){
    let lastCalled = 0;
    return function(...args) {
        let context = this;
        if((Date.now() - lastCalled) > limit) {
            fn.apply(context, args);
            lastCalled = Date.now();
        }
    }
}
```
You might all be connoisseurs in JS, but I didn't have any idea what this apply did or how this fitted into any of this. But I had some knowledge, so I did something like this.
```js
let obj1 = {
    foo: 'bar',
    fn: function() {
        console.log(this.foo);
    }
}

let throttledFn = throttle(obj1.fn, 1000);

throttledFn(); // logged undefined
throttledFn(); // didn't log anything
```
Well the crazy thing was it logged undefined. Why on earth would it log undefined? I did the crazy apply thing and yet things didn't work well for me. So I had to put up a question on StackOverflow Chat to figure out what was happening.
So one guy wrote how how exactly I should write it for the things to work and here it is.
```js
let obj2 = {
    foo: 'bar',
    fn: throttle(function() {
        console.log(this.foo);
    }, 1000)
}

obj2.fn();
obj2.fn();
```
Well, that's not much different from what I wrote first, is it? But apparently, it is. 
The thing is context changes in both places. In obj1, the function is called outside the context. Whereas if you look at obj2, it is called in context. 
So obj2 will look like something like this when executed:
```js
{
    foo: 'bar',
    fn: function(...args) { // this function is the return value of the closure
        let context = this;
        if((Date.now() - lastCalled) > limit) {
            (function() { // this is the fn that was passed to the closure.
                console.log(this.foo);
            }).apply(context, args);
            lastCalled = Date.now();
        }
    }
}
```
I know this looks a bit crazy, but I didn't have a choice I swear. Let's simplify the above a bit and write a nested function inside a new object.
```js
"use strict"
// using strict mode is crazy awesome as it helps your 
// already crazy program go a little less crazy
let newObj = {
    foo: 'bar',
    fn: function() {
        console.log("first level of this", this); // => { foo: 'bar', fn: '[Function: fn] }
        function nestedFn() {
            console.log("second level of this", this); // => undefined
        }
        nestedFn();
    }
}

newObj.fn();
```
So the first log statement logs the correct this but the second one logs undefined. That's weird. What's weirder is if it wasn't for the strict mode, it would have logged the global object. 
This is why we need to use apply to add context. So if we need the object's this inside the nested function, we will write something like this:
```js
"use strict"
// using strict mode is crazy awesome as it helps your 
// already crazy program go a little less crazy
let newObj = {
    foo: 'bar',
    fn: function() {
        console.log("first level of this", this); // => { foo: 'bar', fn: '[Function: fn] }
        function nestedFn() {
            console.log("second level of this", this); // => { foo: 'bar', fn: '[Function: fn] }
        }
        nestedFn.apply(this);
    }
}

newObj.fn();
```
and this would give you the right answer.

## but this is not the end of this
After all this, if you thought that was all the madness with this, you're wrong. It works differently when you call it from a callback function. Say for example, when you write something like this 
```js
let lufthansa = {
    companyName: 'Lufthansa',
    buyPlane() {
        console.log(`${this.companyName} brought a plane`);
    }
}

document
    .queryselector('.buy')
    .addEventListener('click', lufthansa.buyPlane)
```
If you expected to see Lufthansa brought a plane I am sorry to let you know that, you'll see undefined brought a plane. This is because, the this inside the callback function will be the this of the button with class name buy.
You have to bind the function like this if you want to get the right output:
```js
document
    .queryselector('.buy')
    .addEventListener('click', lufthansa.buyPlane.bind(lufthansa))
```

In the meanwhile, if you're planning to buy a laptop or want to read up on the internal workings of a laptop or what the components are, check out [rustedreview](https://rustedreview.com/).