KAJAN M

Determining the value of `this` in JS (Flowchart included)

May 13, 2021

JavaScript
everything about this in JavaScript
Photo by Hari Nandakumar

Few weeks before, while I was sharing my React knowledge with one of my friend, we stumbled upon an issue, in which I found my mental model on this was wrong 🙈😬. Thereafter, I went through different blog posts, tutorials and, it took me some time to understand the principles behind different this bindings. I think the knowledge worth sharing. So here we go.

Table of Contents


Introduction


this in JavaScript can be very confusing if we don't understand the concepts behind it. Firstly, it does not work like the familiar "lexical scope". Secondly, it behaves very differently compared to other Object Oriented Programming Languages like C# and Java. However, it is not yet another corner case of the language. The underlying principles are not complex either.* It is just that we have a wrong mental model. It is just that we don't have a strong JavaScript foundation. In this post, we will see, how we candetermine the value of `this` easily.
* I'll take it back 😅.

Vote of thanks


I am thankful to Kyle Simpson and the FrontEndMasters team for the awesome Deep JavaScript Foundations - v3 course. While, this post discusses this deeply, the principles I’ve learnt from the course are very valuable, helpful and laid me a strong foundation. Thank you sir!

I shared this post in r/JavaScript subreddit, and a generous person from the community gave some valuable feedback. I’ve updated the post accordingly. Thank you so much senocular 🙏🤗😘.

What is this


If the question is ‘What is this in C# or Java?’, the answer is simple. this is a keyword that points to the current instance of the class. Using this we can access the instance properties, methods, and accessible properties and methods from the inheritance chain. That’s it, deterministic, sweet, and simple.

However, we can’t approach this in JavaScript with the same mental model. Unlike in Object Oriented Programming languages, this in JS is not deterministic but flexible. It can take different values depending on how the function is invoked.

Ok, back to the question. What is this in JS?

Some guides identify `this` as a reference to the current execution context. While it gives some tangible idea it's not strictly correct though. As per the ECMAScript language specification this is only a part of the execution context. I think saying, "normally this points to the current Environment Record would be more appropriate". I'm not sure though. Please feel free to share your thoughts in the comments.
Here is the link to the spec if you're interested.

Anyway, let’s forget about giving a special name to this and let’s try to understand it by the different values it can take, and the logic behinds it.

Demonstrating the dynamic nature of this


Before diving into the nitty-gritty details about this, let’s see some contrived examples where this in a function takes different values depending on how the function is invoked.

In the below example, we invoke the same function logThis in different ways (only the ‘how’ part changes). I’ve commented out the logged this values right next to each statement. See if your understanding aligns with them. Also, feel free to copy the code into the browser developer console and play around with it. If anything doesn’t make sense to you, don’t worry, by the end of this post, you’ll be able to determine the value of this with a smile 😀, well that’s the goal of this post.

function logThis(description) {
// debugger
console.log(description, this)
}
let obj = {
name: "obj",
logThis,
}
let anotherObj = {
name: "anotherObj",
logThis,
}
logThis("normal function invocation in non-strict mode") //globalThis (window in browser)
obj.logThis("invoke as a method of `obj`") //obj
anotherObj.logThis("invoke as a method of `anotherObj`") //anotherObj
let logThisVar = obj.logThis
logThisVar("assign method of obj to a variable and invoke it") //globalThis (window in browser)
logThis.call(obj, "invoke using .call") //obj
new logThis("invoke the function using new") //{}

What values does this take


In the global scope(outside any function), `this` points to the global object.Inside an ES Module(the `export`, `import` thing or <script type="module"/>), in the top-level, i.e. outside any function `this` is `undefined`.
Inside a function, normally*, this points to an object. It can be the global object, an object we created, a DOM element or a new object created on the fly for us by the JS engine -- some object.

this inside arrow functions


Arrow functions do not define their own this. So the concept of binding a value to this does not apply. In other words, inside arrow functions, this is just a variable that does not exist in the function’s scope.

What do we do if we come across a variable that is not in the current scope? Aha, we check if the variable exists in the parent scope. And, if the parent scope also does not have that variable? No problem, we look at its grand parent’s scope. Similarly, we follow the scope chain, until we hit the root of the scope chain which is the global scope where this is set to the global object. (Strict mode/non-strict mode doesn’t matter).

If the term “lexical” is not clear to you, we can think of it as “static”. So we can translate “lexical scope” as “static scope”. Ok, I think I’m not making any sense.

Let me approach differently.

Look through the below example. What do you think will be logged when we call sayHelloWr?

var name = 'panda';
function sayHello() {
console.log(`Hi, my name is ${name}`)
}
function sayHelloWr() {
var name = 'karady'
sayHello()
}
sayHello() // Hi, my name is panda
sayHelloWr() // Hi, my name is ???

If you guessed “Hi, my name is panda”, 🥂, you can skip to the next section.

JavaScript is a two-pass system, in the first pass, a plan for which variable go into which scope is fixed(static). In the second pass, whenever we refer a variable, which variable to take is decided based on the plan created in the first pass. In other words, a variable-scope mapping is created based on where things are defined.

So since, arrow functions do not define their own this, since it’s going to be resolved from the scope chain lexically, where the arrow function is defined matters!

this inside non-arrow functions


Inside non-arrow functions, which object this refers to entirely depends on how we invoke the function/method. We can’t just look at the function definition(where the function is defined) and say what value this will take. We need to look at how the function is invoked.

This is one of the key takeaways! In contrast to JavaScript's (lexical)scope system in which variables are resolved based on "where they're defined", this is determined in based on the call site.

Different ways to invoke a function (different this bindings)


So, we’ve seen, the value of this in a function depends on the call site, “how we/something else invokes the function”

But, you might ask, how else can we invoke a function other than how we normally do? Well, it turns out there are four main ways to invoke a function.

  1. Invoke a function with default this binding
  2. Invoke a function with implicit this binding
  3. Invoke a function with explicit this binding
  4. Invoke a function using the new keyword
Let's take a look at each of them.

Function invocation with default this binding


In this category, we directly invoke the function using its name.

In this scenario, the this value is set to the global object in sloppy(non-strict) mode.

In strict mode, this is undefined.

Here is a contrived example.

var name = 'global'; // var outside any function will attach to the global object, this is same as saying `this.name= 'global'`
function sayHello() {
console.log(`Hi, my name is ${this.name}`);
}
function sayHelloStrict() {
'use strict';
console.log(`Hi, my name is ${this.name}`);
}
const panda = {
name: 'panda',
sayHello,
}
sayHello(); // Hi, my name is global
const pandaSayHello = panda.sayHello;
pandaSayHello(); // Hi, my name is global
sayHelloStrict(); // TypeError (this is undefined)

Note that this in pandaSayHello also resolves to the global object instead of the panda object. This is because pandaSayHello() invocation falls into the default this binding category and not into the implicit this binding category.

Function invocation with implicit this binding


In this approach, we access the method using the object dot notation or using the square brackets.

function sayHello() {
console.log(`Hi, my name is ${this.name}`);
}
let panda = {
name: 'panda',
sayHello
}
let karady = {
name: 'karady',
sayHello
}
panda.sayHello(); // Hi, my name is panda
karady.sayHello(); // Hi, my name is karady
panda['sayHello'](); // Hi, my name is panda

In this scenario, this inside the method is implicitly set(or bound) to the object on which the method is invoked.

So when we say panda.sayHello(), we’re asking the JS engine to invoke the function sayHello with its this bound to the panda object. Thus, this.name = panda.name = ‘panda’.

Function invocation with explicit this binding


JavaScript offers APIs that allow us to explicitly set the value for this. This is useful when passing a this-aware function as a callback or when authoring libraries. Remember, when we pass a function as an argument, the passed function gets assigned to a new variable. So, now this will rely on how that variable is invoked, right?

We can use call, apply methods to invoke a function with an explicit this value.

If we want to explicitly set the this value but defer the execution(i.e execute the function later), then we can use the bind method.

Let’s see some examples for each category.

Explicit this binding using ‘call’ or ‘apply’


function sayHello() {
console.log(`Hi, my name is ${this.name}`);
}
let panda = {
name: 'panda',
sayHello,
}
let karady = {
name: 'karady',
sayHello,
}
sayHello.call(panda); // Hi, my name is panda
sayHello.apply(karady); // Hi, my name is karady
karady.sayHello.call(panda); // Hi, my name is panda

Note that we invoke call on the this aware function using the dot notation as if it’s a method on it.

In this scenario, this is initialized to the first argument passed to the call, apply methods.

Also, explicit binding takes precedence over implicit binding. Thus karady.sayHello.call(panda) will log “Hi, my name is panda”.

Explicit this binding using bind


This is also called hard binding. In contrast to call, apply methods, bind does not immediately invoke the function. It returns a new function instead.

The bind method internally uses apply to achieve hard binding.

function sayHello() {
console.log(`Hi, my name is ${this.name}`);
}
let panda = {
name: 'panda',
}
const sayHelloPanda = sayHello.bind(panda);
sayHelloPanda(); // Hi, my name is panda

Overriding this of a hardbound function


Say we have a hardbound function* and for some reason we want to set a new this value.

None of the above approaches(i.e. invoking the hardbound function with implicit/explicit/default bindings) will work (yes, calling bind again also won’t work)

Later, we’ll see about invoking a function using the new keyword. Doing so will override the this value to a new object who is linked to the object pointed by function’s prototype property. This is like resetting the this value to the original one. More on this later.

Explicitly binding primitive values to this


In strict mode, `this` takes what we pass to `.call/.apply or .bind`. There is no special rule in strict mode.

However, in sloppy mode, explicit binding of primitive values involves boxing.

Primitive value means, a value that is not an object. So, there are no members(properties, methods, getters, .etc).

For example,

var age = 20 // variable age is assigned a primitive value

In sloppy mode, when we try to explicitly bind a primitive value to this, the value is automatically converted to an object using the corresponding constructor *. Also, when explicitly binding null/undefined, this will point to the global object.

Function invocation using the new keyword


We can use the new keyword to invoke a function. The new keyword hijacks the function call and does the following things.

  1. creates a new empty object
  2. links* that empty object to another object pointed by the (constructor)function's prototype property**
  3. calls the function with this set to the new object (usually the function modifies this by adding new properties)
  4. returns this if the constructor function does not return any object (if the constructor function returns a primitive value, that will be ignored and, the newly created this will be returned instead)

Due to the linking in step 2, by the time we access this, all the properties and functions added to the prototype chain can be accessed through it.

Below is a contrived example.

function sayHello() {
console.log(`Hi, my name is ${this.name}, ${this.sing()}`);
}
// if this part is confusing, hold on plz
// in the next section
// we'll discuss about adding methods to prototype
sayHello.prototype.sing = function() {
return 'Jingle bells, jingle bells'
}
new sayHello(); // Hi, my name is undefined, Jingle bells, jingle bells

It might seem rather unusual to invoke a function using new. It’s common with constructor functions and ES6 classes. I’ve put a contrived example, just to show, it’s the new keyword that is doing the magic.

In the following section, we’ll take a close look at a typical constructor function. Though, it’s the new keyword, that is doing the this binding and, there is no special this binding rule within a constructor function, understanding constructor functions, will help us to better understand the new operator and why it exists.

A typical constructor function


Constructor functions are just normal functions. They’re called so since they’re used to constructing new “objects”. Constructor functions are usually this-aware and intended to be called using the new operator.

Well, in JavaScript we already have various ways to create new objects, so why do we need a constructor function and another operator to invoke it.

The challenge here is not just to define a blueprint to construct new objects in a reusable way but, how do we mimic an Object-Oriented Programming language? Mainly how are we going to allow inheritance. Constructor functions, the new operator, the dynamic nature of this, the [[Prototype]] prototype, constructor properties etc. altogether, solve this problem.

Back to constructor functions.

When naming a constructor function, to signal others that it’s a “constructor” function, we use PascalCasing. So that they know, ok I’ve to invoke it using new.

Typically, we don’t explicitly return anything from a constructor function. The new operator will implicitly return the this object in that case.

Below is a contrived example of a typical constructor function and, some explanation about it.

function Person(firstName, lastName) {
// where does `this` come from?
this.firstName = firstName
this.lastName = lastName
// we don't return anything?
}
// what is `Person.prototype`?
Person.prototype.sayHello = function() {
// hmmm, what value would `this` take
console.log(`Hi, my name is ${this.firstName} ${this.lastName}`)
}
let panda = new Person('Panda', 'Karady')
panda.sayHello() // Hi, my name is Panda Karady
// ***************************************
// below is the same code snippet
// with some explanation
// ***************************************
function Person(firstName, lastName) {
// constructor functions are intended to be invoked using `new`
// behind the screen
// before executing the function
// the `new` operator does some extra stuffs
// it creates an empty object
// links it to another object
// then invokes the function with its `this` set to the new object
// usually, inside the function,
// we modify `this` by adding properties to it
// for example
this.firstName = firstName
this.lastName = lastName
// however,
// normally, we don't add methods to the instance directly though
// directly as in `this.sayHello = function(){...}`
// why?
// because we have a better way
// to not to redefine methods on every instance
// to share a method across all instances
}
Person.prototype.sayHello = function() {
// so we add methods to another shared object
// it's not just another random object
// it's pointed by the function's prototype property
// in our example - `Person.prototype`
// let's call it "prototype object"
// any instance created by invoking a function using `new`
// will have a hidden linkage([[Prototype]]) to the "prototype object"
console.log(`Hi, my name is ${this.firstName} ${this.lastName}`)
// but, wait,
// if all instances share the same method,
// what value would `this` take?
// `this` in JavaScript is dynamic!
// to determine the value of `this`
// we need to look at the call site
}
let panda = new Person('Panda', 'Karady')
// the panda variable points to
// the (implicitly)returned `this` value
panda.sayHello() // Hi, my name is Panda Karady
// remember,
// `sayHello` is not a direct member of the returned instance(`this`)
// it's resolved from the prototype chain (the hidden [[Prototype]] linkage)
// and, if it matters,
// lexical scope and prototype chain are different things

this inside ES6 classes


ES6 class syntax offers a declarative approach to define constructor functions. People with OOP language background will find the syntax familiar and easier to get started with. However, under the hood, an ES6 class is a function and, the concepts of prototypical inheritance, dynamic this binding etc. still holds true.

There’re some differences though. For example classes are not hoisted, they default to strict mode, and there is no strict equivalent to super keyword in pre ES6 etc. Albeit these differences, under the hood, ES6 class is still a function. So, when creating a new instance from a class, ultimately we’re calling the function with the new operator.

In the previous sections, we’ve discussed how the new operator sets this to a newly created(linked) object. The same principle applies to ES6 classes as well. However, there are some inconsistencies on what value this would take inside different elements of a class. In the following sections, we’ll look about it and, the reason behind it.

Value of this inside ES6 class constructor


Similar to other OOP languages, ES6 class can have optional constructor. It’s a function. It’s name should be constructor (in OOP languages, constructor functions take the class name instead, also unlike in OOP languages, in JavaScript, constructors can explicitly return some value).

If we don’t specify a constructor, JS engine automatically creates one for us. If we explicitly specify a constructor, JS engine will use that instead. constructor is useful to perform initialization logic.

The body of the ES6 `constructor` will become part of the mapping constructor-function body.

Below is a contrived ES6 class example with constructor.

class Base {
constructor(name) {
this.name = name
// doMoreStuff
}
}

The following is the equivalent constructor function representation.

function Base(name) {
this.name = name
// doMoreStuff
}

So, when asking “what is the value of this inside an ES6 class constructor”, essentially, we’re asking, what is this inside the equivalent mapping constructor function body.

Well, we know the answer. The function is supposed to be invoked using the new operator. So, this will point to an empty object, that is linked to the prototype chain.

Value of this inside derived class constructor


The new operator behaves differently when it comes to derived classes.

Usually, the new operator creates an empty object and sets it to this, so, by the time we do this.someKey = someValue, this is already initialized to an empty object. However, inside derived constructors, the new operator expects the parent constructor to set a value to this. So, until the parent constructor is executed, the value of this inside the derived constructor will be undefined. Thus, any attempt to set a property on this(which is undefined) will result in ReferenceError.

To avoid this problem, we are asked to do super(), before doing anything with this inside the derived constructor.

What is this super() thing? What is it doing behind the screen? How does it affect the this value?

We can think super() as invoking the parent constructor using the new operator and, setting the returned value to this . i.e. this = new Base()

Such translation is not exactly correct, but will help us to understand the value of this inside the derived constructor. Also, let’s imagine Base as a normal constructor function instead of a class. With that loose assumptions, let’s break down the steps involved in this = new Base()

  1. The new operator creates an empty object
  2. The new operator links it to the prototype chain
  3. The new operator invokes the Base constructor function with its this set to the newly created linked object
  4. The function usually adds some properties to this
  5. If the function returns a reference type(i.e. non-primitive value), the new operator returns that value, otherwise, the new operator returns this
  6. Returned value is assigned to this variable inside Derived constructor

So, usually, this inside derived constructor will be an object linked to the prototype chain and will have all the properties defined in the parent constructor.

But, the catch here is, what happens if the parent constructor explicitly returns a reference type value other than its this? What would be the value of this inside the derived constructor in such case? You get the point right, this will point to the returned object which might be linked to some different prototype chain.

Below is a contrived example that illustrates how base constructor affects the value of this inside the derived constructor.

class Base {
instanceFieldInBase = 'base'
constructor() {
return [1, 2, 3]
}
}
class Derived extends Base {
constructor() {
super()
// though, not strictly correct
// we can think of super() as this = new Base()
console.log(this instanceof Base) // false
console.log(this instanceof Array) // true
}
}
let derived = new Derived()
console.log(derived instanceof Base) // false
console.log(derived instanceof Derived) // false
console.log(derived instanceof Array) // true

Value of this inside instance fields


Instance fields become properties of `this`.

Suppose we’ve defined an instance field like below.

class Base {
instanceField = this
anotherInstanceField = 'hello'
}

It’s really a shortcut to adding a property to this inside the constructor.

class Base {
constructor() {
this.instanceField = this
this.anotherInstanceField = 'hello'
}
}

The equivalent constructor function would be.

function Base() {
this.instanceField = this
this.anotherInstanceField = 'hello'
}

We know the value of this inside the constructor function body when invoking it using the new operator. this will point to an empty object that is linked to the prototype chain.

If it’s a derived class, the this object will also have the instance fields from the base class as its property.

If the base constructor explicitly returns a reference type for some reason, then this will point to that returned value.

Value of this inside instance methods


Instance method of an ES6 class, becomes member of the object pointed by the prototype property of the mapping constructor function.

Below is an example of ES6 class instance method, followed by the equivalent constructor function representation.

class Base() {
instanceMethod() {
console.log(this)
}
}
function Base() {}
// instance methods become property of the prototype object
// that way the same method can be shared with all instances
// since all instances are linked to the prototype chain
// they'll be able to access the method
Base.prototype.instanceMethod = function() {
console.log(this)
}

We usually invoke the method as if it’s a direct member of the instance. This falls into the implicit this binding category and, in such case, this will point to the instance on which the dot notation is used.

However, do note that instance methods are not hardbound functions and can point to different `this` values depending on the call site.

Value of this inside base instance method when accessing through derived instance


Due to the prototypical inheritance we can access the base instance methods through the instance of the Derived class. The base instance methods will be part of the Base class’ prototype object. However, as already discussed, where the function/method is defined doesn’t matter. Pay attention to the call site instead. Usually, as discussed in the previous section, this will fall into the implicit this binding category.

Value of this when accessing through the super keyword


We can access a method defined in the base class using super. It’s helpful if we’re overriding the method in the derived class. To access the base method we can do super.methodName() inside the derived method. Due to the dot notation, this falls into the implicit this binding category. So, this will point to super, but, what is super?

We’ve already used super() to invoke the parent constructor. Since it’s invoked, you might think super is a function, not really! If invoked as a function it’ll call the base constructor. In other places, super will point to this but, with a special look-up behavior. If we access a method on super, it’ll get it from the base class’ prototype object. If you need to visualize the value of super, just visualize the value of this in that context where super is used (remember the special lookup behavior though).

Below is a contrived example that illustrates the discussed concept.

class Base {
instanceFieldDefinedInBase = 'base'
instanceMethod() {
console.log('inside base method')
console.log(this.instanceFieldDefinedInBase, this.instanceFieldDefinedInDerived)
}
}
class Derived extends Base {
instanceFieldDefinedInDerived = 'derived'
// we override the base method
instanceMethod() {
// this.instanceMethod() will cause infinite loop
super.instanceMethod()
console.log('inside derived method')
}
}
const derived = new Derived()
derived.instanceMethod()
// inside base method
// base derived
// inside derived method

Value of this inside static fields


Static fields become properties of the mapping constructor function. To access the static property, we need to use the dot notation on the function. Remember we discussed how JavaScript treats functions as first-class objects?`this` in a static field will always point to the class where it's defined.

Below is an ES6 class syntax with a static field followed by its equivalent constructor function representation.

class Base {
static staticField = 'static-field'
static anotherStaticField = this.staticField
}
function Base {}
Base.staticField = 'static-field'
Base.anotherStaticField = Base.staticField
// note that, how `this` is replaced by the function name
// it's fixed, and will continue to point to the function object

Value of this inside derived static fields


Static fields are inherited. So, we can access a static field defined in the Base class, using the Derived class.
class Base {
static staticFieldDefinedInBase = 'base'
}
class Derived extends Base {}
console.log(Derived.staticFieldDefinedInBase) // base

Now, what if the static field from Base class refers to this? What would this refer when accessing through the Derived class?

In the previous section, we saw, how this was replaced by the class(function) name. So, even if we access through the `Derived` class, `this` will continue to point the `Base` class.

Take a look at the below example, if you want to understand how we can map ES6 subclass to a constructor function and how derived class inherits base class static fields.
class Base {
static staticFieldDefinedInBase = this
}
class Derived extends Base {
static staticFieldDefinedInDerived = this
}
// ***************
console.log(Derived.staticFieldDefinedInBase) // Base
console.log(Derived.staticFieldDefinedInDerived) // Derived
function Base() {}
Base.staticFieldDefinedInBase = Base
// see, `this` is replaced with the function name
// so, even if we access this property using `Derived`
// it will still point to `Base`
function Derived() {
// `_super` points to the `Base` function
// we'll set up it later
this._super.call(this)
}
Derived.staticFieldDefinedInDerived = Derived
// `Object.create` will create an empty object
// we can pass an argument
// which will be set to the internal [[Prototype]] property of the new object
Derived.prototype = Object.create(Base.prototype)
Derived.prototype.constructor = Derived
Derived.prototype._super = Base
// this line sets `[[Prototype]]` of `Derived` to `Base`
// so, if a property/method is missing in `Derived`
// the JS engine will then look at `Derived`
Object.setPrototypeOf(Derived, Base)
// ***************
console.log(Derived.staticFieldDefinedInBase) // Base
console.log(Derived.staticFieldDefinedInDerived) // Derived

Value of this inside static methods


Static methods become methods of the mapping constructor function(object).

Below is an ES6 class syntax with a static method followed by equal constructor function representation.

class Base {
static staticMethod = function() { console.log(this) }
}
function Base() {}
Base.staticMethod = function() { console.log(this) }

There is no special this binding rule here. As usual, this can take different value depending on how the method is invoked. Typically, it will fall into the implicit this binding category and, in such case, this will point to the constructor function.

Value of this inside base static methods


Similar to static fields, static methods are also inherited. So, we can access a static method defined in the Base class using Derived class.

There is no special this binding rule here. To determine the value of this, look at the call site. Generally, it will fall into the implicit this binding category.

Value of this when invoking a method on a primitive value


Primitive values are non-object types. So they don’t have any property or method. However, if we try to invoke a method or access a property on a primitive value, the JS engine will implicitly convert the value to the corresponding wrapper object (aka boxing).

Say we add a this aware method to the wrapper function’s prototype object like below.

const getFirstChar = Symbol()
// Symbol() gives us a guaranteed unique value
// this will help us to prevent naming collisions
String.prototype[getFirstChar] = function() {
// validation logic goes here
return this[0]
}

Usually, we’ll invoke the function as below.

"panda"[getFirstChar]()

This falls into the implicit this binding category. So, you might guess, the value of this will be the primitive string “panda”. That’s only if we’re in strict mode (usually we are and we should).

In sloppy mode `this` will point to the wrapped object instead.
String.prototype.someFunction = function() {
console.log(typeof this == 'string')
// true in strict mode, false in sloppy mode 🤷‍♂️
}

Flowchart illustrating the steps in determining the value of this


Ok, we’ve seen a lot of things. this might still seem a bit confusing. Hope this flowchart will help.

Flowchart illustrating the basic steps in determing the value of `this`

Please find below the updated flowchart with some more scenarios covered.

Flowchart illustrating complete steps in determining the value of `this`

Summary


  • Arrow functions do not define their own this, it’s just a variable that doesn’t exist in the function’s scope and will be resolved lexically.
  • To determine the value of this inside arrow functions, we need to look where the arrow function is defined.
  • If the arrow function is defined inside a non-arrow function, then depending on how that parent function is invoked, this inside arrow function also might point to different values.
  • To determine the value of this inside non-arrow functions, we need to look at the call site.
  • If the function is invoked using the new keyword, then a brand-new object is set to this.
  • We can explicitly set a value to this using call, apply and bind methods.
  • When the function is invoked as a method(using object dot notation), this points to the context object. This is also called as “implicit binding”.
  • When we invoke a function just using its name(functionName()), this is undefined in strict mode and points to the global object(window variable in Browser) in sloppy mode. This is called as “default binding”.

TODO


Initially, I thought to include these topics too. But, I’m already exhausted thinking about this 😂. For now, I’ll just leave some notes here.

  • talk about setTimeout, addEventListener
  • arrow function vs hardbound function using .bind
  • this inside accessor properties and proxies
  • why not just make this behave as in other languages — talk about prototype chain
  • include ES6 class example to the invoke-using-new section
  • does bind has performance impact?
  • var that = this and other hacks

Changelog


17/05/2021


Address feedback Reddit comment #1, Reddit comment #2

  • this inside top level of an ESM
  • this inside different elements of a (ES6)class
  • explicit this binding of primitive values in sloppy, strict modes
  • overriding this of a hardbound function
  • indicate why explicit this binding and other binding concepts do not directly apply to arrow functions
  • add todo to talk about accessor properties and proxies

27/05/2021


Address feedback Reddit comment #3

  • provide more context about the prototype object and internal [[Prototype]] property
  • explain about typical constructor function
  • revamp the this inside ES6 class section, remove Babel tranformation screenshots and explain in words, align subsections to have a logical connectivity
  • add a section to discuss different implicit this binding behavior when trying to invoke a method on a primitive value
  • update flowchart

Kajan
👋 Hi! Welcome to my blog. I'm Kajan, a Software Engineer focusing on ASP.NET Core, EF Core and JavaScript(Vue, React) stack. I am thankful to the internet community for helping me out on various occasions 🙏😘. I hope to give back to the community by sharing my experience, and knowledge.
Twitter
GitHub