DEV Community

Yug Jadvani
Yug Jadvani

Posted on

1 1

A Deep Dive into new and this in JavaScript: Unlocking the Power of Object-Oriented Programming

JavaScript is a powerful, flexible language with roots in functional programming and capabilities for object-oriented programming (OOP). Two critical concepts that lie at the heart of OOP in JavaScript are new and this. While these keywords may seem straightforward, they have nuances that can be challenging to master, even for experienced developers. In this blog post, we’ll take a deep dive into the workings of new and this in JavaScript, breaking down their behaviors with examples and best practices.


Table of Contents

Introduction to this in JavaScript

At its core, this is a context-dependent keyword that refers to the object from which a function is called. Unlike some other languages where this is statically bound, in JavaScript, the value of this can change depending on how and where a function is invoked.

In simple terms:

  • Global Scope: In the global context (or non-strict mode), this refers to the global object (window in browsers, global in Node.js).
  • Inside Methods: Within an object method, this refers to the object that owns the method.
  • Event Handlers: In event listeners, this typically refers to the element that triggered the event.

We will explore these contexts with examples later in the blog.


Understanding new in JavaScript

The new keyword in JavaScript is used to create instances of user-defined objects or built-in objects like Date, Array, etc. When you use new with a constructor function, it creates a new object and binds this to that object, essentially linking it to a prototype.

For example:

function Car(make, model) {
  this.make = make;
  this.model = model;
}

const myCar = new Car("Tesla", "Model 3");
console.log(myCar); // { make: 'Tesla', model: 'Model 3' }
Enter fullscreen mode Exit fullscreen mode

When new is used:

  1. A new empty object is created.
  2. The this keyword inside the constructor is set to reference this new object.
  3. The function executes its code and assigns properties to this.
  4. The object is linked to the constructor’s prototype, enabling inheritance.
  5. The function returns this unless an object is returned explicitly.

How new Works Under the Hood

Let's simulate the behavior of new using a custom function:

function simulateNew(constructor, ...args) {
  const obj = {}; // Step 1: Create a new empty object
  Object.setPrototypeOf(obj, constructor.prototype); // Step 2: Link the object to the constructor's prototype
  const result = constructor.apply(obj, args); // Step 3: Bind this and execute the constructor
  return result instanceof Object ? result : obj; // Step 4: Return the object
}

function Person(name) {
  this.name = name;
}

const john = simulateNew(Person, "John Doe");
console.log(john.name); // John Doe
Enter fullscreen mode Exit fullscreen mode

This function follows the same steps as the new keyword, demonstrating the behind-the-scenes mechanics.


Examples of this in Various Contexts

  1. Global Context

In the global context (non-strict mode), this refers to the global object (window in browsers).

console.log(this === window); // true

function showThis() {
  console.log(this); // window
}

showThis();
Enter fullscreen mode Exit fullscreen mode

In strict mode ('use strict';), this is undefined in the global context:

"use strict";

function showThis() {
  console.log(this); // undefined
}

showThis();
Enter fullscreen mode Exit fullscreen mode
  1. Object Method Context

When this is used inside an object method, it refers to the object that owns the method.

const person = {
  name: "Alice",
  greet() {
    console.log(this.name); // 'Alice'
  },
};

person.greet();
Enter fullscreen mode Exit fullscreen mode

Here, this refers to the person object because it’s the context in which the greet method is called.

  1. Constructor Function Context

In a constructor function, this refers to the newly created object.

function Animal(type) {
  this.type = type;
}

const dog = new Animal("Dog");
console.log(dog.type); // Dog
Enter fullscreen mode Exit fullscreen mode
  1. Event Handler Context In event handlers, this refers to the DOM element that triggered the event.
const button = document.querySelector("button");

button.addEventListener("click", function () {
  console.log(this); // the button element
});
Enter fullscreen mode Exit fullscreen mode

When using arrow functions in event listeners, this is lexically bound and does not refer to the element:

button.addEventListener("click", () => {
  console.log(this); // refers to the outer scope (e.g., window)
});
Enter fullscreen mode Exit fullscreen mode

Best Practices and Common Pitfalls

  1. Arrow Functions and this: Arrow functions do not bind their own this; instead, they inherit this from the surrounding lexical context. This can be useful in situations like event handlers or callbacks where you want to maintain a reference to the parent scope.
function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++;
    console.log(this.seconds);
  }, 1000);
}

const myTimer = new Timer(); // Correctly logs the seconds count
Enter fullscreen mode Exit fullscreen mode
  1. Explicit Binding with .call(), .apply(), and .bind(): You can manually control the value of this using these methods. For example:
function greet() {
  console.log(this.name);
}

const person = { name: "John" };
greet.call(person); // John
Enter fullscreen mode Exit fullscreen mode
  1. Avoid using this in global functions: It’s generally a good practice to avoid this in global functions, as it can lead to unexpected behaviors, especially in strict mode.

  2. Class Syntax: Since ES6, using the class syntax provides a more intuitive way to define constructor functions with this and new.

class Car {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
}

const myCar = new Car("BMW", "X5");
console.log(myCar.make); // BMW
Enter fullscreen mode Exit fullscreen mode

Conclusion

The new and this keywords play a pivotal role in JavaScript's object-oriented paradigm, allowing for the creation and management of objects and their behavior. Understanding how this works in different contexts and how new constructs instances of objects is crucial for writing robust, scalable JavaScript code. By mastering these concepts, you can avoid common pitfalls and write cleaner, more maintainable code.

Keep experimenting and writing examples to solidify your understanding of these core JavaScript concepts!


Enjoyed the read? If you found this article insightful or helpful, consider supporting my work by buying me a coffee. Your contribution helps fuel more content like this. Click here to treat me to a virtual coffee. Cheers!

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay