Using new.target in Class Constructors
In Chapter 3, you learned about new.target
and how its value changes depending on how a function is called. You can also use new.target
in class constructors to determine how the class is being invoked. In the simple case, new.target
is equal to the constructor function for the class, as in this example:
class Rectangle {
constructor(length, width) {
console.log(new.target === Rectangle);
this.length = length;
this.width = width;
}
}
// new.target is Rectangle
var obj = new Rectangle(3, 4); // outputs true
This code shows that new.target
is equivalent to Rectangle
when new Rectangle(3, 4)
is called. Class constructors can’t be called without new
, so the new.target
property is always defined inside of class constructors. But the value may not always be the same. Consider this code:
class Rectangle {
constructor(length, width) {
console.log(new.target === Rectangle);
this.length = length;
this.width = width;
}
}
class Square extends Rectangle {
constructor(length) {
super(length, length)
}
}
// new.target is Square
var obj = new Square(3); // outputs false
Square
is calling the Rectangle
constructor, so new.target
is equal to Square
when the Rectangle
constructor is called. This is important because it gives each constructor the ability to alter its behavior based on how it’s being called. For instance, you can create an abstract base class (one that can’t be instantiated directly) by using new.target
as follows:
// abstract base class
class Shape {
constructor() {
if (new.target === Shape) {
throw new Error("This class cannot be instantiated directly.")
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super();
this.length = length;
this.width = width;
}
}
var x = new Shape(); // throws error
var y = new Rectangle(3, 4); // no error
console.log(y instanceof Shape); // true
In this example, the Shape
class constructor throws an error whenever new.target
is Shape
, meaning that new Shape()
always throws an error. However, you can still use Shape
as a base class, which is what Rectangle
does. The super()
call executes the Shape
constructor and new.target
is equal to Rectangle
so the constructor continues without error.
I> Since classes can’t be called without new
, the new.target
property is never undefined
inside of a class constructor.