The rules of this

·

3 min read

This

The this keyword is a dynamic binding that belongs to the environment record of a function’s execution context and whose main goal is dynamically accessing different objects in order to reuse code. More on that here:

I think I wanted to explain the difference between arrow and normal functions, but I kinda explained this and closures by accident

The value this refers to is some object (yes, an object; always; functions are objects, but it’s unlikely it refers to a function object).

this can be dynamic since a new execution context is created for a function each time it’s invoked.

It’s a dynamic binding, but there are 4 rules that may help us understand what value this points to. Let’s review them.

Oh, by the way, if you know Kyle Simpsons, yes, this is basically a rebranded explanation of his writings about the rules of this. Go read his books if you want to go deep since they are light years away compared to this article.

4 rules of this

Default binding

function foo() {
  console.log(this); 
}
foo(); // the global object if not in strict mode, undefined in strict mode

Implicit binding: resolves to the context object.

const obj = {
  x: '',
  foo() {
    console.log(this);
  }
}

obj.foo(); // { x: '', foo() { ... } }

Explicit binding

  • Soft binding: apply, call.

  • Hard binding: bind.

function foo() {
  console.log(this); 
}

const obj = {
  x: '',
}

foo().call(obj); // obj
foo().apply(obj); // obj
const boundFoo = foo().bind(obj);
boundFoo(); // obj

new binding

function Fn(x) {
  console.log(this); 
  this.x = x;
  console.log(this); 
}

const myFn = new Fn(''); // logs {} first and { x: '' } second
``

Wait, how did that even happen!?

new does 4 things:

  1. Creates a brand new object under the hood.

  2. Makes sure the brand new object will be the value of the this keyword in the function environment record of the execution context created when invoking the function being called with new (in the example, Fn).

  3. Prototypally binds the brand new object's to the .prototype of the function being called with new (in the example, Fn.prototype).

  4. Invokes the function and returns the brand new object (unless the function returns some other object).

So in the example:

  • At the time Fn is called, the this keyword points to an object with no properties, but prototypally bound to Fn.prototype.

  • this.x = x adds an x property to that object.

  • The object is returned.

Rules of precedence

From more to less (more precedents override less precedents):

  1. new binding.

  2. Hard explicit binding (bind). *A custom version (not the one provided by JS) of bind could override the new binding, placing it as first., since JS's bind algorithm specifically takes the new keyword into account making sure it doesn't override it.

  3. Soft explicit binding (call, apply).

  4. Implicit binding.

  5. Default binding.

Quirks

  • Arrow functions behave differently. Arrow functions don't define local bindings for this. That means the function environment record slot for this will be empty. The value of the this keyword in an arrow function will be resolved to the value of this in its function environment record’s outer function environment record, the one of the execution context where the arrow function was defined. So it will be resolved through the same mechanism as any user-authored binding.

  • Beware: Some APIs, frameworks and libraries sometimes bind the this keyword. i.e., when the browser calls an event handler, it will bind the this keyword to the DOM node the event handler is attached to.