js中 new 一个对象的过程

27 阅读2分钟

使用构造函数

// 这是一个构造函数 (产品蓝图)
function Person(name, age) {
  this.name = name;
  this.age = age;
  
  // 假设构造函数有一个返回值
  // return { custom: 'object' }; // 情况 A
  // return 'a string'; // 情况 B
}

// 为所有“人类”产品添加一个共享的技能
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

// 下达生产指令
const alice = new Person('Alice', 30);

js中new一个对象的过程

在内存中创建一个空对象,

将这个空对象的内部原型 [[Prototype]]  (也就是非标准的 __proto__ 属性) 指向构造函数的 prototype 对象

空对象 现在继承了 构造函数prototype 上的所有属性和方法。

构造函数的 this 关键字绑定到空对象上,然后调用这个构造函数,并传入参数

在构造函数执行完毕后,隐式地 return 这个空对象(此时可能已经被赋值) 。

  • 如果构造函数自己显式地 return 了一个值

    • 如果返回的是一个对象 (Object) 或函数 (Function)  (情况 A):那么 new 操作符会放弃我们自己创建的那个 newObj,而是直接返回构造函数返回的这个对象

      
      function SpecialPerson() {
        this.name = 'default';
        return { custom: 'object' }; // 返回了一个对象
      }
      const sp = new SpecialPerson();
      console.log(sp); // 输出: { custom: 'object' } (我们自己的 newObj 被丢弃了)
      
    • 如果返回的是一个基本数据类型 (String, Number, Boolean, null, undefined) (情况 B):那么 new 操作符会忽略这个返回值,仍然坚持返回我们自己创建的那个 newObj

      function NormalPerson() {
        this.name = 'default';
        return 'a string'; // 返回了一个基本类型
      }
      const np = new NormalPerson();
      console.log(np); // 输出: { name: 'default' } (返回值 'a string' 被忽略了)
      
  • 最佳实践:在构造函数中,通常不应该使用 return 语句,除非你知道你在做什么。让 new 自动地、隐式地返回 this 是最清晰、最符合预期的行为。

手写实现 new

function myNew(Constructor, ...args) {
  // 步骤 1: 创建一个全新的空对象
  const newObj = {};

  // 步骤 2: 链接到原型
  Object.setPrototypeOf(newObj, Constructor.prototype);

  // 步骤 3: 绑定 this 并执行构造函数
  const result = Constructor.apply(newObj, args);

  // 步骤 4: 返回新对象 (并处理构造函数的返回值)
  // 判断构造函数的返回值是不是一个对象或函数
  const isObject = typeof result === 'object' && result !== null;
  const isFunction = typeof result === 'function';

  return isObject || isFunction ? result : newObj;
}

// --- 使用我们自己的 myNew ---
const bob = myNew(Person, 'Bob', 40);
bob.sayHello(); // 输出: Hello, my name is Bob
console.log(bob instanceof Person); // true