Deep Analysis of JS-new Operational Constructor

Now look at a familiar piece of code:

function Parent(name) {
    this.name = name;
    this.say = function () {
        return this.name;
    };
}
Parent.prototype.age = 18;

var child = new Parent("pch");

console.log(child); // Output: Parent {name:'pch', say: [Function]}
console.log(child.name); // Output: pch
console.log(child.say()); // Output: pch
console.log(child.age); // Output: 18

When it comes to inheritance, we must first talk about the constructor new Function();
So how is the new operator implemented internally?
How does it create an instance of a user-defined object type or an instance of a built-in object with a constructor?

Referring to the information, we know that the internal operation is:

  1. Create an empty simple JavaScript object (that is {});
  2. Link the object (that is, set the constructor of the object) to another object;
  3. Take the newly created object in step 1 as the context of this;
  4. If the function does not return an object, it returns this.

Now we implement an analog constructor _new() with the same function as new Function():

// 1. Create our objective function first
function Parent(name) {
    this.name = name;
    this.say = function () {
        return this.name;
    };
    return {};
}

// Prototype also adds point attributes to test prototype chain inheritance
Parent.prototype.age = 18;

// 2. Create our _new simulation constructor:  
//    function _new() { }

// 3. How to call the analog constructor?
//    We assume this call: var child = new (Parent, "pch");

// 4. Define the calling rules and implement the internal logic of the function.

function _new() { 
    // 4.1. We process parameters
    // Verify that the first parameter is a function. It's not a function that returns prompt information
    if (Object.prototype.toString.call(arguments[ 0 ]) !== "[object Function]") {
        throw "The first parameter must be a function.";
    }
    
    // 4.2. Create an empty simple JavaScript object (that is {});
    
    var obj = {};
    
    // 4.3. Link the object (that is, set the constructor of the object) to another object (what I think is the object function object, bind the prototype);
    obj.__proto__ = arguments[ 0 ].prototype;
    
    // 4.4. Take the newly created object in Step 1 as the context of this (probably by switching this, we can use apply to pass parameters);
    var res = arguments[ 0 ].apply(obj, Array.prototype.slice.call(arguments).slice(1));
    
    // 4.5. If the function does not return an object, it returns this (roughly speaking, when the objective function does not have a return value, it returns a new object, otherwise it returns the return value of the objective function).
    // After testing, when using the new operator, the return value of the objective function is returned as soon as it has a return value, and we do the same.
    return res === undefined ? obj : res;
}

// 5. Testing our functions

var child = _new(Parent, "pch");

console.log(child); // Output: Parent {name:'pch', say: [Function]}
console.log(child.name); // Output: pch
console.log(child.say()); // Output: pch
console.log(child.age); // Output: 18

// Test error reference
try {
    _new(1, 2, 3);
} catch (e) {
    console.log(e); // Output: The first parameter must be a function.
}

// Test the objective function with return value
function ParentHasReturn() {
    return "pch";
}

console.log(_new(ParentHasReturn)); // Output: pch

// 6. Summary: The test was successful.

Supplementary content:

General rules:

  • Objects have proto attributes and functions have prototype attributes.
  • Objects are generated by functions.
  • When an object is generated, the _proto_ attribute of the object points to the prototype attribute of the function.

For more information, please move: My blog

Tags: Javascript Attribute

Posted on Wed, 09 Oct 2019 15:58:45 -0700 by koddos