JavaScript in-depth series, through the simulation of new, takes you to uncover the truth of using new to obtain constructor instances
new
One sentence introduction new:
The new operator creates an instance of a user-defined object type or one of the built-in object types with constructors
Maybe it's a little difficult to understand. Before we simulate new, let's see what functions new has achieved.
for instance:
// Otaku otaku, short for house function Otaku (name, age) { this.name = name; this.age = age; this.habit = 'Games'; } // Because of lack of exercise, physical strength is worrying Otaku.prototype.strength = 60; Otaku.prototype.sayYourName = function () { console.log('I am ' + this.name); } var person = new Otaku('Kevin', '18'); console.log(person.name) // Kevin console.log(person.habit) // Games console.log(person.strength) // 60 person.sayYourName(); // I am Kevin
From this example, we can see that the instance person can:
- Access the properties in the Otaku constructor
- Visit otaku Properties in prototype
Next, we can try to simulate it.
Because new is a keyword, it cannot be overwritten directly like the bind function, so we write a function named objectFactory to simulate the effect of new. This is how it works:
function Otaku () { ...... } // Use new var person = new Otaku(......); // Using objectFactory var person = objectFactory(Otaku, ......)
Preliminary implementation
analysis:
Because the result of new is a new object, we also need to create a new object when simulating the implementation. Suppose this object is called obj, because obj will have the attributes in the Otaku constructor. Think of the classic inheritance example, we can use Otaku Apply (obj, arguments) to add new attributes to obj.
In the first part of the JavaScript in-depth series, we talked about prototypes and prototype chains. We know the importance of examples__ proto__ The attribute will point to the prototype of the constructor. It is precisely because of this relationship that the instance can access the attributes on the prototype.
Now we can try to write the first edition:
// First edition code function objectFactory() { var obj = new Object(), Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; Constructor.apply(obj, arguments); return obj; };
In this edition, we:
- Create an object obj in the way of new Object()
- Take the first parameter, which is the constructor we want to pass in. In addition, because shift will modify the original array, the first parameter of arguments will be removed
- Point the prototype of obj to the constructor, so that OBJ can access the properties in the constructor prototype
- Using apply, change the constructor this to point to the new object, so that obj can access the properties in the constructor
- Return obj
More about:
Prototype and prototype chain, you can see JavaScript in depth: from prototype to prototype chain
apply, you can see JavaScript in depth: Simulation Implementation of call and apply
Classic inheritance, you can see JavaScript deep inheritance
Copy the following code into the browser, and we can do the following tests:
function Otaku (name, age) { this.name = name; this.age = age; this.habit = 'Games'; } Otaku.prototype.strength = 60; Otaku.prototype.sayYourName = function () { console.log('I am ' + this.name); } function objectFactory() { var obj = new Object(), Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; Constructor.apply(obj, arguments); return obj; }; var person = objectFactory(Otaku, 'Kevin', '18') console.log(person.name) // Kevin console.log(person.habit) // Games console.log(person.strength) // 60 person.sayYourName(); // I am Kevin
[]~( ̄▽ ̄)~**
Return value effect implementation
Next, let's look at another case. If the constructor has a return value, for example:
function Otaku (name, age) { this.strength = 60; this.age = age; return { name: name, habit: 'Games' } } var person = new Otaku('Kevin', '18'); console.log(person.name) // Kevin console.log(person.habit) // Games console.log(person.strength) // undefined console.log(person.age) // undefined
In this example, the constructor returns an object, and only the properties in the returned object can be accessed in the instance person.
And also note that here we return an object. What if we just return a value of a basic type?
Another example:
function Otaku (name, age) { this.strength = 60; this.age = age; return 'handsome boy'; } var person = new Otaku('Kevin', '18'); console.log(person.name) // undefined console.log(person.habit) // undefined console.log(person.strength) // 60 console.log(person.age) // 18
The result is completely reversed. Although there is a return value this time, it is equivalent to no return value for processing.
So we also need to judge whether the returned value is an object. If it is an object, we will return this object. If not, we will return what we should return.
Let's look at the code of the second and last version:
// Code of the second edition function objectFactory() { var obj = new Object(), Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; var ret = Constructor.apply(obj, arguments); return typeof ret === 'object' ? ret : obj; };