JavaScript Essentials: Often Overlooked Elements of One of The Most Popular Web Development Languages

Omar Verduzco
SSENSE-TECH
Published in
7 min readOct 15, 2021

The first of a 3-part series on JavaScript essentials by SSENSE-TECH

JavaScript is one of the most popular languages for web development nowadays. At SSENSE, we take advantage of the powerful, mature, and stable JavaScript ecosystem. Additionally, JavaScript’s versatility allows us to develop applications that bring great performance and value to the company both in the back-end and front-end. Several of our microservices run on NodeJS and are developed in Typescript.

Despite all the benefits that the language offers, it comes with some quirks and is often criticized for being overly flexible, not strongly typed, and for allowing (and sometimes even enforcing) bad practices. In addition to this, the design of the language itself is awkward for many developers that are used to other languages like Java, where it is more intuitive how to properly implement Object-Oriented Programming (OOP) principles. This, paired with a complexity of specific concepts that I will cover in this series, leads developers to overlook some of the language’s essentials, opting instead for pre-built libraries and modifying as little as they can to make things work. This introduces error-prone workarounds to the parts that they don’t fully understand.

In this JavaScript Essentials series, I will explain some key concepts that are usually misunderstood or ignored. Part 1 focuses on the this keyword.

WHAT IS this

Just like in other languages, this refers to the instanced object where any property or method will be searched for when accessed. However, JavaScript has a different set of rules that could be less intuitive. The first thing to keep in mind is: this is assigned a value at the moment of execution, not at the moment of declaration. This operation is also known as binding.

Another way to picture this, however simplified, is to imagine who is going to call your function at the moment of execution. If no specific object is calling your function, it will be bound to the global object.

This rule seems to be counter-intuitive because developers need to think about the runtime when designing and writing their code. However, there are clear rules that, when understood, make it easier for anyone to use this instead of finding ways to avoid it.

DEFAULT BINDING

You can think of this as the fallback mechanism when there is no explicit indication of what this should mean.

Think of this rule whenever a function is going to be invoked as a standalone function; that means, it’s not a method of an object and there is no enforced meaning of this. For such a scenario, this will be the global object, unless restrictions apply when running in strict mode. There are many examples to illustrate the point, a clear example is the callback function passed as argument to the filter method in an array:

This code will throw an error and it may be hard to figure out the reason, why did the runtime try to find the isPair method on an undefined object? First, we need to know that functions are first class citizens, meaning that you can interact with them as if they were any other object, which also means that you can pass them as arguments to other functions. Functions passed as arguments are known as callbacks, and they are always executed as standalone invocations, which also means that the default binding rule applies and this will be bound to the global object. The reason why the last line throws is that I ran the code in strict mode, and access to the global object is restricted.

This is something that goes against the usage of this in other languages, because we’re using it inside a class, expecting it to refer to the instantiated object.

So, how do we solve this problem?

EXPLICIT BINDING

JavaScript provides three ways of forcing the meaning of this into the function execution: bind, call, and apply. call and apply execute the function by forcing the meaning of this to the object passed as argument:

bind returns a function that has this set to the object passed as argument. This binding is one of the possible solutions to the Array.filter problem stated at the default binding section:

The difference between bind and call is that bind doesn’t execute the function, it builds a function that you can execute later, while call does execute the function right away.

IMPLICIT BINDING

This type of binding provides a context to the function invocation. It is basically calling an object’s method, and the runtime will understand that the object will be used for any reference to this inside the method.

new BINDING

This type of binding can be more intuitive, because it is a way to build objects. In JavaScript, you can build objects from functions called constructor functions and, since this is a function execution, it also needs a this binding:

In this case, four steps will happen:

  1. An empty object {} is created as soon as the constructor function is called with the new operator.
  2. That empty object is bound to that specific function execution, so any reference to this inside the function will take effect on that object.
  3. The object is [[Prototype]]-linked to the constructor function.
  4. The return value of the function execution is the newly created object.

ARROW FUNCTIONS

This is not just a fancy and shortened way to create functions. Arrow functions have several differences when compared to their regular counterpart, and one of them helps us solve the challenge of inadvertently losing the binding to this.

The syntax is as follows: a parenthesis that encloses all the arguments passed to the function, a “fat arrow”, and the brackets that will contain the body of the function.

The main difference is that this kind of function doesn’t have its own binding to this, and it will take it from the context that contains it. This is another way to solve our callback function problem highlighted on the default binding section as we can see below:

In this example, even if the arrow function is still executed as a standalone function, the runtime knows that there is no specific binding to this and that it has to interpret it as the enclosing context of the function, which in this case is the object that contains it.

Because of these features, this type of function is not suitable for call, apply, or bind.

Arrow functions have other functional and syntactic differences, like the fact that they can’t be used as constructors like regular functions, and that they do not have the prototype property that allows for prototype inheritance. Explaining all the other differences is not in the scope of this article. Please refer to the Additional resources and related links section to learn more.

CONCLUSION

JavaScript can be a powerful language given that it has one of the biggest communities and ecosystems. It can be a great fit for almost any project and there is a framework or library for practically everything, and it even has amazing supersets like Typescript for strongly typed code. If you dedicate the time to understand its principles, instead of trying to find ways to avoid the concepts that are not intuitive, you can benefit from an amazing developer experience.

At SSENSE we strongly believe that mastering the concepts of the language and runtime explained in this series of articles brings a lot of value to our product, because we create code that follows best practices, is maintainable, as well as scalable. We have a vast group of developers with a solid JavaScript background, and we’ve learned to understand the quirks of the language to take advantage of this powerful and flexible ecosystem.

Stay tuned for Part 2 of this SSENSE-TECH JavaScript Essentials series, which covers asynchronous execution.

ADDITIONAL RESOURCES AND RELATED LINKS

Editorial reviews by Deanna Chow, Liela Touré, and Pablo Martinez. Want to work with us? Click here to see all open positions at SSENSE!

--

--