Traditionally JavaScript is prototype based programming language and developers used to implement Object Oriented Programming concepts in JavaScript using normal functions. That means, ES5 JavaScript did not have classes to implement OOP and developers used to implement the same using functions.

ES6 standards introduced classes into JavaScript. Though most of the new applications uses classes to implement OOP, we can still use functions as object constructors as the underlying JavaScript did not change.

Example of function as a constructor:

function PersonCreator(id, fullName) {
    this.id = id;
    this.fullName = fullName;
}

let john = new PersonCreator(123, 'John Doe')
console.log(john.fullName) // John Doe

The above function can be used as an object constructor without any problems by using the new keyword before the function call. (one of the very common pattern followed by JavaScript developers to distinguish between normal functions and constructor functions is the by giving the capital letter to the first letter of the constructor function) .

The problem that may arise here is, though we need to use the above function to create objects, we can still call it as a normal function. This may happen unintentionally when the developer forget to use the new keyword before the function call.

Suppose, we do the same without new keyword

function PersonCreator(id, fullName) {
    this.id = id;
    this.fullName = fullName;
}

let john = PersonCreator(123, 'John Doe')
console.log(john.fullName) // Cannot read properties of undefined (reading 'fullName')

In the code above, since we are calling the function PersonCreator without new keyword, the return value of the function will be assigned into the variable john. Since we are not returning anything undefined will be assigned into john. So we are getting the error Cannot read properties of undefined.

The problem do not end here. Since, we are attaching the passed parameters to this inside the function, the values will be attached to the function’s parent object.

example:

function PersonCreator(id, fullName) {
    this.id = id;
    this.fullName = fullName;
}

let john = PersonCreator(123, 'John Doe')

console.log(fullName) // John Doe

We can prevent such things from happening by using the pseudo-property new.target

What is new.target in JavaScript ?

new.target is a pseudo-property using which a developer can check if a function is called by using the new keyword or not. If a function is called without new keyword, then the new.target inside that function will be undefined. If it is called with the new keyword, then the new.target will that function itself.

How to use new.target in JavaScript ?

Example code of using new.target inside a function.

function PersonCreator(fullName) {
    console.log(new.target)
    this.fullName = fullName;
}

let john = new PersonCreator('John Doe')

Output:

in NodeJS terminal:

[Function: PersonCreator]

In Chrome console:

ƒ PersonCreator(fullName) {
    console.log(new.target)
    this.fullName = fullName;
}

If you omit new in the above code, then you will be undefined in both node and browser console.

new.target in arrow functions

We can use new.target inside Arrow functions. If you try to use it, you will get an Uncaught SyntaxError: new.target expression is not allowed here.

example:

PersonCreator = (fullName) => {
    console.log(new.target) // Uncaught SyntaxError: new.target expression is not allowed here
    this.fullName = fullName;
}

new.target in classes

Unlike constructor functions which can be called without new keyword, we cannot do the same with the classes.

example:

class Person { }

let john = Person(); // TypeError: Class constructor Person cannot be invoked without 'new'

Though the previous type of scenario may not happen with classes, you can however use it for any other purpose. For example, if you have a parent class and a child class, you may want to know which type of object is being created when the constructor of parent class gets executed.

example:

class Mobile {
    constructor() {
        console.log(new.target.name)
    }
}

class IPhone extends Mobile {
    constructor() {
        super()
    }
}

class Pixel extends Mobile {
    constructor() {
        super()
    }
}

let m1 = new Mobile() // Mobile
let m2 = new IPhone() // IPhone
let m3 = new Pixel() // Pixel