All about "this" in JavaScript
Tags: javascript
Published: 2019-08-01 02:26:15

The `this` keyword is often a source of confusion and headache for people just starting to learn JavaScript so I decided to write down my 7 "rules" for how to figure out what `this` is.


### 1.) Global Context

By default any function that's declared using the `function` keyword on the global scope, basically any function not declared within an object or a function called with `new` in front of it, will have a `this` of the global context. For browsers, the global context is `window` and for node the global context is `global`.

```javascript
function printThis() {
  console.log(this);
}

printThis(); // this will print window in your browser
```

So given the code above, just calling the function `printThis` will always print out `window`.


### 2.) To the left, to the left

Once the `printThis` function is placed on an object and is called through that object, the context, or `this`, will change to the object it's attached to. The key concept here is that the function needs to be placed on an object __AND__ needs to be called through that object. If either of those are not true, then this rule doesn't apply.

```javascript
function printThis() {
  console.log(this);
}

const obj = {
  print: printThis
};

obj.print(); // this will print obj
printThis(); // this will still print window
```

The easiest way to figure out what the context is for any function is to look to the left of the function name and see what it's "attached" to. In the code above, since the `print` function is "attached" `obj`, the `this` when `print` is invoked will be `obj`.

When we call `printThis`, there's nothing to the left of `printThis` so for now, we can safely assume that the context of `printThis` will be the global context. 


### 3.) Apply 

Using the `apply` method we can change what the `this` is for any function. Often this is used to pull out a function that's already attached to an object and call it, just this one time, with a context that's not what it normally would be. 

In other words, `apply` allows us to borrow a function from anywhere and call that function, just this once, with a different context than what it normally would be. 


```javascript
function printThis() {
  console.log(this);
}

const obj = {
  print: printThis
};

const newContext = {
  name: "I'm the context now"
};

obj.print.apply(newContext); // this will print `newContext`
```

The `apply` method's first argument is always the new context that we want this function to act in. This will only "apply" for that one line of code. Calling `obj.print()` on any other line, acts like it normally would. 

Apply can take in a second argument and that is always an array. This array should hold, in order, any arguments that you want to send to the `print` function. I won't touch any more on that particular topic but I do need to highlight that one point, so we can talk about...


### 4.) Call

Call basically does the exact same thing as `apply` but the main difference is that instead of passing in an array for the second argument, you can pass in as many arguments as `print` would normally take.


```javascript
function printThis() {
  console.log(this);
}

const obj = {
  print: printThis
};

const newContext = {
  name: "I'm the context now"
};

obj.print.call(newContext); // this will print `newContext`
```

The mnemonic I like to use is `apply = array, call = commas`. In all the cases so far though, there is some sort of visual indicator of what the context will be on that line of code. If there's none, then you can safely (for now) assume that the context will be the global context. At least, until you see the word...


### 5.) Bind

Bind works much like Call and Apply but the biggest difference here is that bind doesn't fire the function immediately. What bind will do, is return a copy of the original function but with the new context __PERMANENTLY__ "bound". 

```javascript
function printThis() {
  console.log(this);
}

const obj = {
  print: printThis
};

const newContext = {
  name: "I'm the context now"
};

// newPrint's this will always be 'newContext'
const newPrint = obj.print.bind(newContext);
// will print newContext. Visual indicators be damned.
newPrint();


// even if we put 'newPrint' on an object
// then call it like Rule 2, it will still print 'newContext'
obj.boundPrint = newPrint;
// this will still print 'newContext' instead of 'obj' like we would expect with Rule 2.
obj.boundPrint() 

```

The original function is left the same. Remember that `bind` will create a copy of the original function, so that original function gets to stay how it is in terms of context. Unless you created the original function using...


### 6.) Arrow Functions

The only time `bind` won't change the context of a function, is if that function was declared using [Arrow Functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). I won't go into all the details of arrow functions but one of the core parts to arrow functions is that the `this` for arrow functions is determined lexiconically. That's just a fancy word for saying, the `this` is determined by checking what the context was when the arrow function was written. 

So far, all the functions we've looked at have been written in the global context which means that whenever you use an arrow function, the context in which it was declared will be the global context or `window`. Even if the function was declared in an object, the context is still the global context.

```javascript
// arrow function on the global scope
const arrowFunction = () => {
  console.log(this);
};

arrowFunction(); // will print 'window'


// arrow function in an object
const obj = {
  print: () => { console.log(this); }
};
obj.print() // will print 'window'


// even call/apply/bind can't change the context
const boundPrint = obj.print.bind(obj);
boundPrint(); // will still print window
```

This seems nice and all but then when does the context of an arrow function ever change? 


### 7.) The New Keyword

The `new` keyword is special in JavaScript because it can turn a function into a class and with that creates a new "functional context." This is hard to explain, so I'll try to show it in code.

```javascript
// using new in front of Person will turn this function into a class
function Person(name) {
  // using `new` in front of Person it also creates a new context here
  // so all functions within here will have a `this` of person
  // where person is an instance of Person
  this.name = name;

  const print = () => {
    console.log(this.name); // this will always be an instance of Person
  };

  return {
    print: print
  };
}

// person === instance of the class Person
// Person === a class called Person
const person = new Person('Alice');

// somebody === instance of the class Person
// Person === still the same class Person
const somebody = new Person('Bob');

person.print()   // will print 'Alice'
somebody.print() // will print 'Bob'

// bind can't change the print's context since it's an arrow function
const calvin = { name: 'Calvin' };
const boundPrint = person.print.bind(calvin);
boundPrint(); // this will still print 'Alice'

```

I won't get too much into OOP but the `new` keyword is crucial for creating a new "context" and any functions declared within the class will inherit that new "context" as its this. This applies to both arrow functions and function created using the keyword `function`. This is different from declaring a function within any regular function. The context for both will depend on rules 1 - 5. But once we have a new keyword in front of the outer function, then Rule 7 is in effect. 

I know that the rules for `this` are probably still a bit confusing but I hope that I cleared it up a bit. Also, if you're ever unsure what `this` is, you could just run through these rules one at a time. Or even better, just console.log what `this` is. Then refer back to the rules to figure out what just happened. 

And that's it. The 7 ways `this` can be set in JavaScript! Please let me know if I've made any mistakes or if I missed a way `this` can be set. 


**Edit**: 
There are functions in the browser that can change your context (`this`), such as `.addEventListener`. In these situations, the context will always be the object `.addEventListener` is attached to. So, it's best not to use arrow functions here since the context for said arrow function will be bound to where it's written. In most cases, where it's written will not match the object you're attaching this event listener to. I think this is a special case for Rule #2.