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.
Different ways to invoke a function (different this
bindings)
Function invocation using the new
keyword
this
inside ES6 class constructorthis
inside derived class constructorthis
inside instance fieldsthis
inside instance methodsthis
inside base instance method when accessing through derived instancethis
when accessing through the super
keywordthis
inside static fieldsthis
inside derived static fieldsthis
inside static methodsthis
inside base static methodsFlowchart illustrating the steps in determining the value of this
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. 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 šš¤š.
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?
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.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.
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) {// debuggerconsole.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`") //objanotherObj.logThis("invoke as a method of `anotherObj`") //anotherObjlet logThisVar = obj.logThislogThisVar("assign method of obj to a variable and invoke it") //globalThis (window in browser)logThis.call(obj, "invoke using .call") //objnew logThis("invoke the function using new") //{}
this
takeglobalThis
is a special property that we can use to get the global this
value(the global object) regardless of the environment(browser, node, worker).var doTopLevelModuleVariableNotPolluteGlobalScope = true
, well that variable is only available in that module scope unless exported. That's the whole point of modules, right? Encapsulation!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
can take primitive values too. Later we'll see how we can pass any value to this
using explicit binding(Though we can set primitive values to this
, I don't see a good reason why one might want to do that š¤).this
inside arrow functionsArrow 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).
this
can take different values, we should be aware that this
inside an arrow function can also refer to different objects.this
hardbound to the parent's this
. It's a wrong mental model!this
binding through which we can, well, explicitly set any value to this
. This is not applicable to arrow functions. Do note that, any concept around binding a value to this
is not applicable to arrow functions because they don't define their own this
to bind some value to it!this
in the enclosing scope point to(lexically resolved).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 pandasayHelloWr() // 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
, I found some people use the term "lexical this". I think they say so since, this
inside arrow functions are lexically resolved. Do note that such term is only applicable for arrow functions.this
inside non-arrow functionsInside 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 determined in based on the call site.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.
new
keywordthis
bindingIn 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 globalconst pandaSayHello = panda.sayHello;pandaSayHello(); // Hi, my name is globalsayHelloStrict(); // 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.
this
bindingIn 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 pandakarady.sayHello(); // Hi, my name is karadypanda['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ā
.
this
bindingJavaScript 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.
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 pandasayHello.apply(karady); // Hi, my name is karadykarady.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ā.
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
this
of a hardbound functionSay we have a hardbound function* and for some reason we want to set a new this
value.
this
value is explicitly set using the bind
method š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.
this
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.
.call/.apply or .bind
then it is converted to an object using new Number(1)
. If we pass false
, it is converted to object using new Boolean(false)
. This process is also called boxing (coercion of primitive value to object).new
keywordWe can use the new
keyword to invoke a function. The new
keyword hijacks the function call and does the following things.
prototype
property**this
set to the new object (usually the function modifies this
by adding new properties)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)prototype
property. It's an important property. It plays a mojor role when it comes to inheritance in JavaScript.[[Prototype]]
. The new
operator sets this property to point to the object pointed by function's prototype
property.new
as invoking the function using call
with the first argument being an empty object. i.e. functionName.call({})
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 prototypesayHello.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.
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 = firstNamethis.lastName = lastName// we don't return anything?}// what is `Person.prototype`?Person.prototype.sayHello = function() {// hmmm, what value would `this` takeconsole.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 examplethis.firstName = firstNamethis.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` valuepanda.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 classesES6 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.
this
inside ES6 class constructorSimilar 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.
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.
this
inside derived class constructorThe 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()
new
operator creates an empty objectnew
operator links it to the prototype chainnew
operator invokes the Base
constructor function with its this
set to the newly created linked objectthis
new
operator returns that value, otherwise, the new
operator returns this
this
variable inside Derived
constructorSo, 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.
this
in the derived constructor.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) // falseconsole.log(this instanceof Array) // true}}let derived = new Derived()console.log(derived instanceof Base) // falseconsole.log(derived instanceof Derived) // falseconsole.log(derived instanceof Array) // true
this
inside instance fieldsSuppose weāve defined an instance field like below.
class Base {instanceField = thisanotherInstanceField = 'hello'}
Itās really a shortcut to adding a property to this
inside the constructor
.
class Base {constructor() {this.instanceField = thisthis.anotherInstanceField = 'hello'}}
The equivalent constructor function would be.
function Base() {this.instanceField = thisthis.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.
this
binding is applicable only inside functions. If we assign this
to a variable or to an object property, then, they will continue to point to that same this
object no matter what.constructor
modify the same object that is returned as a result of the new Base()
right?this
inside instance methodsBelow 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 methodBase.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.
this
inside base instance method when accessing through derived instanceDue 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.
this
when accessing through the super
keywordWe 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
?
super
keyword addresses š.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 methodinstanceMethod() {// this.instanceMethod() will cause infinite loopsuper.instanceMethod()console.log('inside derived method')}}const derived = new Derived()derived.instanceMethod()// inside base method// base derived// inside derived method
this
inside static fieldsBelow 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
this
inside derived static fieldsclass Base {static staticFieldDefinedInBase = 'base'}class Derived extends Base {}console.log(Derived.staticFieldDefinedInBase) // base
this
? What would this
refer when accessing through the Derived class?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.class Base {static staticFieldDefinedInBase = this}class Derived extends Base {static staticFieldDefinedInDerived = this}// ***************console.log(Derived.staticFieldDefinedInBase) // Baseconsole.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 laterthis._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 objectDerived.prototype = Object.create(Base.prototype)Derived.prototype.constructor = DerivedDerived.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) // Baseconsole.log(Derived.staticFieldDefinedInDerived) // Derived
this
inside static methodsBelow 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.
this
inside base static methodsSimilar 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.
this
when invoking a method on a primitive valuePrimitive 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 collisionsString.prototype[getFirstChar] = function() {// validation logic goes herereturn 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).
this
aware function.String.prototype.someFunction = function() {console.log(typeof this == 'string')// true in strict mode, false in sloppy mode š¤·āāļø}
Ok, weāve seen a lot of things. this
might still seem a bit confusing. Hope this flowchart will help.
Please find below the updated flowchart with some more scenarios covered.
this
, itās just a variable that doesnāt exist in the functionās scope and will be resolved lexically.this
inside arrow functions, we need to look where the arrow function is defined.this
inside arrow function also might point to different values.this
inside non-arrow functions, we need to look at the call site.new
keyword, then a brand-new object is set to this
.this
using call
, apply
and bind
methods.this
points to the context object. This is also called as āimplicit bindingā.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ā.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.
setTimeout
, addEventListener
.bind
this
inside accessor properties and proxiesthis
behave as in other languages ā talk about prototype chainbind
has performance impact?var that = this
and other hacksAddress feedback Reddit comment #1, Reddit comment #2
this
inside top level of an ESMthis
inside different elements of a (ES6)classthis
binding of primitive values in sloppy, strict modesthis
of a hardbound functionthis
binding and other binding concepts do not directly apply to arrow functionsAddress feedback Reddit comment #3
[[Prototype]]
propertythis inside ES6 class
section, remove Babel tranformation screenshots and explain in words, align subsections to have a logical connectivityimplicit this binding
behavior when trying to invoke a method on a primitive value