Simulation Implementation of new of Js handwriting function

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:

  1. Access the properties in the Otaku constructor
  2. 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:

  1. Create an object obj in the way of new Object()
  2. 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
  3. Point the prototype of obj to the constructor, so that OBJ can access the properties in the constructor prototype
  4. Using apply, change the constructor this to point to the new object, so that obj can access the properties in the constructor
  5. 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;

};

Tags: Javascript Front-end

Posted by sanch on Sun, 31 Jul 2022 21:56:00 +0530