How JavaScript Works

In this case, things are not so straight-forward and this is exactly the reason (or at least one of them) why arrow functions were introduced in ES2015, but bear with me, it will all make sense a few paragraphs later.

Besides the difference in syntax between arrow functions (const a = () => { … }) and function declarations (function a() { … }), the value of this inside each is the main difference between the two.

Unlike arrow functions, the value of this inside function declarations is not determined lexically, based on where the function is declared.

It is determined based on how the function is invoked. And there are a few ways you can invoke a function:

  • Simple invocation: myFunction()
  • Object method invocation: myObject.myFunction()
  • Constructor invocation: new myFunction()
  • DOM event handler invocation: document.addEventListener(‘click’, myFunction)

The value of this inside myFunction() is determined differently for each of these types of invocation, independent of where myFunction() is declared, so let’s go through them one by one and see how it works.

Simple invocation

Simple invocation is simply invoking a function like in the example above: The function name alone, without any preceding characters, followed by () (with any optional arguments inside, of course).

In the case of simple invocation, the value of this inside the function is always the global this, which, in turn, points to the global window object, as described in one of the sections above.

That’s it! But remember, this is true for simple invocation only; the function name followed by (). No preceding characters.

Note: Because the value of this in a simple function invocation is actually a reference to the global window object, using this inside functions which are meant to be invoked by simple invocation is considered bad practice.

This is because any properties attached to this inside the function are actually attached to the window object and become global variables, which is bad practice.

This is why, in strict mode, the value of this in any function invoked by simple invocation is undefined, and the example above would output false.

Object method invocation

When a property of an object has a function as its value, it is considered a method of that object, hence the term method invocation.

When this type of invocation is used, the value of this inside the function will simply point to the object on which the method is invoked, which is myObject in the example above.

Note: If the arrow function syntax would have been used, instead of the function declaration in the example above, the value of this inside that arrow function would have been the global window object.

This is because its parent execution context would have been the global execution context. The fact that it is declared inside an object does not change anything.

Constructor invocation

Another way a function can be invoked is by preceding the invocation with the new keyword as in the example below.

When invoked this way, the function will return a new object (even if it does not have a return statement), and the value of this inside the function will point to that newly created object.

The explanation is a bit simplified (more on MDN), but the point is that it will create (or construct, hence constructor) and return an object to which this will point inside the function.

Note: The same applies when using the new keyword on a class, as classes are actually special functions and have only small differences.

Note: Arrow functions cannot be used as constructors.

DOM event handler invocation

When invoked as a DOM event handler, the value of this inside the function will be the DOM element on which the event was placed.

Note: Notice that, in all the other types of invocation, we invoke the function ourselves.

In the case of event handlers, however, we don’t, we only pass a reference to the handler function. The JavaScript engine invokes the function and we have no control over how it will do it.

Invocation with a custom this value

The value of this inside a function can be explicitly set to a custom value by invoking it using bind(), call(), or apply() from Function.prototype.

The example above shows how each of these work.

call() and apply() are very similar, the only difference being that with apply(), the arguments for the function are passed as an array.

While call() and apply() actually invoke the function with the value of this set to whatever you pass in as the first argument, bind() does not invoke the function.

It returns a new function instead, which is exactly like the function on which bind() was used, but with the value of this set to whatever you pass as an argument to bind().

This is why you see (5, 6) after a.bind(obj), to actually invoke the function returned by bind().

In the case of bind(), the value of this inside the returned function is permanently bound to whatever you pass as the this value (hence the name bind()).

No matter which type of invocation is used, the value of this inside the returned function will always be the one that was provided as an argument. It can only be modified again with call(), bind(), or apply().

The above paragraph is almost entirely true. There has to be an exception to the rule, of course, and that exception is the constructor invocation.

When invoking a function in this manner, by placing the new keyword before its invocation, the value of this inside the function will always be the object returned by the invocation, even if the new function was given another this with bind().

You can check this in the following example:

Here’s an example of how you would use bind() to control the value of this for the click event handler we discussed earlier:

Note: bind(), call(), and apply() cannot be used to pass a custom this value to arrow functions.

Note on arrow functions

You can see now how these rules for function declarations, even though fairly simple, can cause confusion because of all the special cases, and be a source of bugs.

A small change on how a function is invoked will change the value of this inside of it. This can cause an entire chain reaction and that’s why it’s important to know these rules and how they can affect your code.

This is why the people that write the specifications for JavaScript came up with arrow functions, where the value of this is always lexical and determined exactly the same every time, regardless of how they are invoked.


View Original