The Principle and Implementation of the new Operator in JavaScript

The Principle and Implementation of the new Operator in JavaScript

Description
The new operator is used to create an instance of a user-defined object type or of a built-in object with a constructor. It primarily performs the following operations: creates a new object, sets the object's prototype to the constructor's prototype property, executes the constructor function (binding this), and returns the newly created object.

Detailed Steps

  1. Basic Usage Example
    When using new Constructor(), the JavaScript engine implicitly performs the following steps:

    function Person(name) {
        this.name = name;
    }
    const person = new Person("Alice"); // Instantiation
    
  2. Underlying Execution Process

    • Step 1: Create a new, empty plain JavaScript object (i.e., {}).
    • Step 2: Set the prototype (__proto__) of this object to the constructor's prototype property.
      // Pseudo code implementation
      let obj = {};
      obj.__proto__ = Constructor.prototype;
      
    • Step 3: Execute the constructor function with the object created in Step 1 as the this context.
      let result = Constructor.call(obj, ...args); // Pass arguments
      
    • Step 4: If the constructor returns an object, return that object; otherwise, return the object created in Step 1.
      return result instanceof Object ? result : obj;
      
  3. Manually Implementing the new Operator
    Simulate the behavior of new via a function:

    function myNew(Constructor, ...args) {
        // Steps 1 and 2: Create object and bind prototype
        const obj = Object.create(Constructor.prototype);
    
        // Step 3: Execute constructor and bind `this`
        const result = Constructor.apply(obj, args);
    
        // Step 4: Handle return value
        return result instanceof Object ? result : obj;
    }
    
  4. Edge Case Validation

    • When the constructor returns an object:
      function Dog() {
          this.legs = 4;
          return { name: "Mock" }; // Returning an object overrides default behavior
      }
      console.log(new Dog().name); // "Mock" (instead of the `legs` property)
      
    • When the constructor returns a primitive value:
      function Cat() {
          this.breed = "Siamese";
          return "abc"; // Primitive values are ignored
      }
      console.log(new Cat().breed); // "Siamese"
      
  5. Prototype Chain Verification
    Objects created via the implementation should correctly inherit prototype methods:

    Person.prototype.sayHello = function() {
        console.log(`Hello, I'm ${this.name}`);
    };
    const p = myNew(Person, "Bob");
    p.sayHello(); // Should correctly output "Hello, I'm Bob"
    

Key Points

  • The essence of the new operator is a three-step process: connecting the prototype chain, binding this, and handling the return value.
  • When implementing manually, note that Object.create is more standard-compliant than directly setting __proto__.
  • If the constructor returns a non-object value, the new operation ignores that return value.