It's late at night...
1. Preface#
Before exploring constructors in JavaScript, we need to understand what a constructor is. As the name suggests, its purpose is to construct something. Next, let's see what it constructs.
Before we continue, we need to clarify that a constructor is actually a function, but it has its own characteristics and usage. However, we will find out how to distinguish and use constructors, which cannot be separated from the new keyword in JavaScript.
2. The new Keyword#
When explaining constructors in JavaScript, we cannot bypass its two characteristics:
- The
thiskeyword is used inside the function body, representing the object instance to be generated. - When creating an object, the
newkeyword must be used.
1. Basic Usage of the new Command#
The following is the most basic usage of the new command to instantiate a constructor:
let MyNew = function (){
this.say_hello = function (){
console.log('hello world')
}
}
let mynew = new MyNew
The above operation uses the new command to make the constructor MyNew generate an instance object, and assigns the result to mynew, so mynew obtains the properties in the constructor.
2. Constructor with Parameters#
In practice, we cannot construct a large number of identical objects with a single constructor, so we can pass parameters when newing a constructor.
let MyNew_1 = function (name){
this.name = name
}
let mynew_1 = new MyNew_1('张三')
console.log(mynew_1.name) // '张三'
The above operation is to pass the corresponding parameters when instantiating an object using the new keyword.
3. Specifications of the new Keyword#
Because when using the new keyword to instantiate a constructor, it will be automatically executed at the end, so you can find out when using the new keyword.
let mynew_2 = new MyNew_1 // Not recommended
let mynew_3 = new MyNew_1() // Recommended
Although both of the above methods can be used, the second one is more recommended. Compared with the first one, it is easier to identify.
4. Note#
When using a constructor, if we forget to use the new keyword, it will be executed as a function.
let MyNew_1 = function (name){
this.name = name
}
let test = MyNew_1()
console.log(test.name) // undefined
The reason why undefined is output in the above example is that when MyNew_1 is treated as a function, its scope is global, and the internal this points to window, but window does not declare this variable, so undefined is finally output.
So how can we avoid such things from happening?
- Use strict mode
In addition to normal running mode, ECMAScript 5 added a second running mode: "strict mode". As the name suggests, this mode makes Javascript run under stricter conditions.
function Fubar(foo, bar){
'use strict';
this._foo = foo;
this._bar = bar;
}
Fubar()
After using strict mode, if you use a constructor as a function, it will throw an error directly. The result is as follows:

The reason for the error is mainly due to the fact that in strict mode, the this inside the function cannot point to the global object by default, and it is equal to undefined, which causes an error when calling without new (JavaScript does not allow adding properties to undefined).
- Use the
instanceofkeyword
We can use the instanceof keyword to judge.
function Fubar_1(foo, bar) {
if (!(this instanceof Fubar_1)) {
return new Fubar(foo, bar);
}
this._foo = foo;
this._bar = bar;
}
console.log(Fubar_1(1, 2)._foo)// 1
console.log((new Fubar_1(1, 2))._foo) // 1
As shown in the above code, we only need to judge whether this is an instance object of Fubar_1 to know whether the constructor uses the new keyword. If not, it will help you new and return the instantiated object.
3. The Principle of the new Keyword#
When we use the new keyword, it actually performs the following steps:
- Create an empty object as the object instance to be returned.
- Set the prototype of this empty object to the
prototypeproperty of the constructor. - Assign this empty object to the
thiskeyword inside the function. - Start executing the code inside the constructor.
The code conversion is as follows:
function _new(/* constructor */ constructor, /* constructor parameters */ params) {
// Convert the arguments object to an array
var args = [].slice.call(arguments);
// Take out the constructor
var constructor = args.shift();
// Create an empty object that inherits the prototype property of the constructor
var context = Object.create(constructor.prototype);
// Execute the constructor
var result = constructor.apply(context, args);
// If the return result is an object, return it directly, otherwise return the context object
return (typeof result === 'object' && result != null) ? result : context;
}
// Example
var actor = _new(Person, '张三', 28);
Continue to learn more later 🧐🧐🧐
4. Return Values in Constructors#
When there is a return statement in the constructor, we just need to remember that if it returns an object, the final return value will be this object, otherwise it will return this.
let MyNew_2 = function (){
this.count = 0
return this.count
}
(new MyNew_2()) === 0 // false
let MyNew_3 = function (){
this.count = 0
return {count:this.count}
}
(new MyNew_3()).count === 0 // true
5. new.target#
This is a new property introduced by ECMASript 6. It has two main functions:
-
When we call a constructor using the
newoperator, the value of thenew.targetproperty is the constructor, otherwise it isundefined.function Person (fullName) { if (typeof new.target !== 'undefined') { this.fullName = fullName; } else { throw new Error('Person must be called with the new keyword.'); } } let student = new Person('aa'); let student_1 = Person('aa'); // Throws an error -
It can check whether
new.targetis called by a specific constructor.function Person (fullName, age) { if (typeof new.target === Person) { this.fullName = fullName; this.age = age; } else { throw new Error('Person must be called with the new keyword.'); } } function Dog (fullName, age) { Person.call(this, fullName, age) } let dog = new Dog('HeHe', 3) console.log(dog)
In es5, if we also want to determine whether a constructor is used, we will think of instanceof for the first time.
Like this:
function Person (name) {
if(this instanceof Person){
this.name = name
} else {
throw new Error('Not called with new')
}
}
var person2 = Person('Hello') // Throws an error
With the above operation, we can determine whether the constructor uses the new keyword. However, it is not good enough, for example, in the following example:
function Person (name) {
if(this instanceof Person){
this.name = name
} else {
throw new Error('Not called with new')
}
}
var person1 = new Person('Hello')
var person2 = Person.call(person1,'Hello') // Executes normally
In the above code, using the call method sets this to an instance of person1. For the function itself, it is impossible to distinguish whether Person.call or the new keyword obtains an instance of Person. However, this can be achieved through new.target 😍😍😍