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. Let's take a look at what it constructs.
Before we continue, we need to clarify that a constructor function is just a function, but it has its own characteristics and usage. However, we will find out how to distinguish and use constructor functions, 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 use of the
this
keyword inside the function body, which represents the object instance to be generated. - The
new
keyword must be used when creating an object.
1. Basic Usage of the new
Command#
The following is the most basic usage of the new
command to instantiate a constructor function:
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 function MyNew
generate an instance object and assign the result to mynew
. mynew
then obtains the properties of the constructor function.
2. Constructor with Parameters#
In practice, we cannot construct a large number of identical objects with a single constructor function, so we can pass parameters when new
ing a constructor function.
let MyNew_1 = function (name){
this.name = name
}
let mynew_1 = new MyNew_1('张三')
console.log(mynew_1.name) // '张三'
The above operation passes the corresponding parameter when instantiating an object using the new
keyword.
3. Specifications of the new
Keyword#
Because when using the new
keyword to instantiate a constructor function, it will be automatically executed at the end, so we 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 to the first one, it is easier to identify.
4. Note#
When using a constructor function, if we forget to use the new
keyword, it will be executed as a normal function.
let MyNew_1 = function (name){
this.name = name
}
let test = MyNew_1()
console.log(test.name) // undefined
The reason why undefined
is output above is that when MyNew_1
is treated as a function, its scope is global, and the internal this
points to window
. Since window
has not declared this variable, it outputs undefined
in the end.
So how can we avoid such things from happening?
- Use strict mode
In addition to normal mode, ECMAScript 5 added a second mode of operation: "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 function as a function, it will throw an error directly. The result is as follows:
The reason for the error is mainly because in strict mode, the this
inside the function cannot point to the global object by default, and 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 determine.
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 determine whether this
is an instance object of Fubar_1
to know whether the constructor function 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 function. - Assign this empty object to the
this
keyword inside the function. - Start executing the code inside the constructor function.
The code equivalent to the above steps 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 function
var context = Object.create(constructor.prototype);
// Execute the constructor function
var result = constructor.apply(context, args);
// If the 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);
Let's continue to learn more 🧐🧐🧐
4. Return Values in Constructors#
When a constructor function has a return
statement, we just need to remember one thing: if it returns an object, the final return value will be that object; if it is not an object, 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 in ECMASript 6. It has two main purposes:
-
When we call a constructor function using the
new
operator, the value of thenew.target
property is the constructor function; 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.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 function is used, we would immediately think of instanceof
.
For example:
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 function 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, the call
method is used to set this
as an instance of person1
. For the function itself, it is impossible to distinguish whether Person.call
or the new
keyword is used to obtain an instance of Person
. However, this can be achieved through new.target
😍😍😍
6. References#
- 王昱潇 --- 判断函数被调用的方法
- 网道 --- 实例对象与 new 命令
- 萌新小吉 --- 严格模式详解