preface
In the process of learning JavaScript, it is inevitable to encounter the new operator, this time to take a good look at the root, but also to deepen understanding and memory.
What is the new operator?
The new operator is defined in MDN like this:
The new operator creates an instance of a user-defined object type or of a built-in object with a constructor.
Have a constructor. What does that mean? Let’s take a look at some examples:
/ / 1
let Animal1=function(){this.name=1};
let animal=new Animal1; // No () is passed
//=>Animal1 {name: 1}
/ / 2
let TestObj={}
let t1=new TestObj;
//=>Uncaught TypeError: TestObj is not a constructor
Copy the code
As you can see, example 1 successfully executed the new statement and created the instance. TypeError: TestObj is not a constructor; TestObj is not a constructor; Why can’t ordinary objects perform the new operator? In the ECMA specification:
If Type(argument) is not Object, return false.
If argument has a [[Construct]]
internal method, return true. Return false.
This means:
- The constructor must first be an object, otherwise the condition is not met
- Second, the object must own
[[Construct]]
Internal methods can be used as constructors
{} is an object that satisfies the first condition, so it must be because {} has no inner method [[Construct]] and therefore cannot be constructed using the new operator.
So now that we have the operable object of the new operator, can we see what it does? The answer is: NO! Let’s look at another example:
/ / 3
let testObj={
Fn(){
console.log("Successful construction!")}}let t3=new testObj.Fn;
//=>Uncaught TypeError: testObj.Fn is not a constructor
Copy the code
what? Why can’t a function that was successfully constructed just now be used as a method? In fact, there is also a direct introduction in MDN:
Methods cannot be constructors! They will throw a TypeError if you try to instantiate them.
This means that a method cannot be a constructor, and if you try to create an instance of a method, you will throw a type error. Let’s look at another example:
/ / 4
const example = {
Fn: function() { console.log(this); },
Arrow: () = > { console.log(this); },
Shorthand() { console.log(this); }};new example.Fn(); // Fn {}
new example.Arrow(); // Uncaught TypeError: example.Arrow is not a constructor
new example.Shorthand(); // Uncaught TypeError: example.Shorthand is not a constructor
Copy the code
In contrast to this example, we look at the ECMA specification and see that all functions are created depending on the FunctionCreate function:
FunctionCreate (kind, ParameterList, Body, Scope, Strict, prototype)
- If the prototype argument was not passed, then let prototype be the intrinsic object %FunctionPrototype%.
- If “kind” is not Normal, let allocKind be “non-constructor”.
The definition of this function can be seen
- Only when the type is
Normal
A function is constructible only when it is created, otherwise it is unconstructible.
In our example, Arrow is of type Arrow and ShortHand is of type Method, so both are not constructible functions. This also explains the “Method cannot be a constructor” stated in Example 3. Now that you know what the new operator can do, you’re ready to see what it does (not easy TAT).
What does the new operator implement?
Let’s take a simple example to see how this works:
function Animal(name){
this.name=name;
console.log("create animal");
}
let animal=new Animal("Rhubarb"); //create animal
console.log(animal.name); / / rhubarb
Animal.prototype.say=function(){
console.log("myName is:"+this.name);
}
animal.say(); / / myName is: rhubarb
Copy the code
Let’s analyze it from this example. First let’s look at this sentence:
let animal=new Animal("Rhubarb");
Copy the code
As you can see, when we do the new operator, we get an animal object, so we know that the new operator must create an object and return that object. Look at this code again:
function Animal(name){
this.name=name;
console.log("create animal");
}
Copy the code
And when we see the result, and we do print Create Animal, we know that the animal function body is being executed in this process, and we’re passing in arguments, and that’s why we’re executing our output statement. But where is this. Name =name in the body of our function? Here it is:
console.log(animal.name); / / rhubarb
Copy the code
After executing the body of the function, we find that the name value of the returned object is the value assigned to this, so it is not difficult to determine that the value of this refers to the newly created object in the process. And then there’s the last paragraph:
Animal.prototype.say=function(){
console.log("myName is:"+this.name);
}
animal.say(); / / myName is: rhubarb
Copy the code
Animal is on the prototype chain of the animal object, so what level is that? Let’s verify:
animal.__proto__===Animal.prototype; //true
Copy the code
Animal’s __proto__ refers directly to animal prototype. In addition, let’s see what happens if we return a value in the body of the constructor:
function Animal(name){
this.name=name;
return 1;
}
new Animal("test"); //Animal {name: "test"}
Copy the code
If the value returned is ignored, let’s return an object:
function Animal(name){
this.name=name;
return {};
}
new Animal("test"); / / {}
Copy the code
We find that the returned instance object is overridden by our return value. Now that we have an overview of the core functionality of the new operator, let’s make a quick summary.
summary
new
The operator does the following:
- Create a new object that will
this
Bind to the newly created object - Call the constructor with the passed argument
- Of the object to be created
_proto__
Object pointing to the constructorprototype
- If the constructor does not explicitly return an object, the newly created object is returned, otherwise the explicitly returned object is returned (as above)
{}
)
The simulation implements a new operator
Finally, let’s implement a new operator
var _myNew = function (constructor, ... args) {
Create a new object obj
const obj = {};
//2. Bind this to the new object and call the function with the passed argument
// To get the first argument, the constructor passed in
// let constructor = Array.prototype.shift.call(arguments);
// Bind this while calling the function... Pass in the parameter expansion
let res = constructor.call(obj, ... args) //3. The object to be created_proto__Object pointing to the constructorprototype
obj.__proto__ = constructor.prototype//4. Determine the final result based on the displayed valuereturn res instanceof Object ? res : obj;
}
Copy the code
This is the more understandable version, and we can simplify it to get the following version:
function _new(fn, ... arg) {
const obj = Object.create(fn.prototype);
const res = fn.apply(obj, arg);
return res instanceof Object ? res : obj;
Copy the code
And you’re done!
conclusion
Starting from the definition, this paper explores the purpose and principle of the new operator, and simulates the implementation of the core functions. It is not difficult to simulate implementing a new operator, but it is more important to understand the process and understand how it works.
Write in the last
I am a little white, may be some information check is not very full or there are mistakes, welcome to point out, will be corrected in time. If you think this article is helpful to you, please click a “like” to support it. Thank you!