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
this
keyword is used inside the function body, representing the object instance to be generated. - When creating an object, the
new
keyword 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 new
ing 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
instanceof
keyword
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
prototype
property of the constructor. - Assign this empty object to the
this
keyword 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
new
operator, the value of thenew.target
property 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.target
is 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
😍😍😍