JavaScript实例化过程中原型链是怎么建立的

作者:异步漫步时间:2026-10-30 18:44:51浏览:273

深入解析JavaScript在执行new操作符实例化对象时原型链建立的底层过程、内存指针绑定机制、核心执行步骤与手写实现。

在 JavaScript 中,当使用 new 关键字实例化一个构造函数时,新对象之所以能立即访问构造函数原型上定义的方法与属性,核心就在于实例化过程中引擎在内存中建立了一条由内部原型指针构成的查找链。简单来说,实例化时引擎创建了一个全新的空对象,并将该新对象的内部隐藏属性 [[Prototype]](在浏览器环境中通常暴露为 __proto__)直接指向了构造函数的 prototype 对象。这一步指针赋值就是原型链建立的关键节点。

一、实例化执行的关键四步流程

当我们执行类似 const student = new Person('小明'); 的语句时,JavaScript 引擎在底层严格遵循标准规范完成以下四个操作:

  1. 创建新对象:在堆内存中开辟一段空间,生成一个新的普通 JavaScript 对象。
  2. 链接原型(建立原型链):将这个新对象的内部槽 [[Prototype]] 指针赋值为构造函数的 prototype 属性所引用的对象。
  3. 绑定 this 并执行构造函数:将构造函数内部的执行上下文 this 绑定到刚刚创建的新对象上,并传入参数执行函数体代码(完成实例自身属性的初始化)。
  4. 决定返回值:检查构造函数的执行结果。如果构造函数显式返回了一个非空对象类型值,则最终返回该对象;否则默认返回第 1 步中创建并完成原型链接的新对象。
JavaScript实例化过程中原型链是怎么建立的
图1:new 操作符四步运行流程图,重点标出了原型指针在执行函数体之前的链接位置

二、内存指针与关系图谱:prototype 与 [[Prototype]]

很多初学者容易混淆函数上的 prototype 属性与实例上的原型指针。在 JavaScript 中,两者的本质分工明确:

  • 构造函数的 prototype:每个普通函数在被声明时,引擎都会为它创建一个公共对象,这就是该函数的 prototype。它包含一个默认的 constructor 属性,指回构造函数自身。这个对象专门作为未来由该构造函数创建的所有实例的“公共仓库”。
  • 实例对象的 [[Prototype]]:任何对象实例都有一个由规范定义的内部插槽,存储对某个原型对象的内存引用。实例本身通常没有 prototype 属性,而是通过 [[Prototype]](代码中可通过 Object.getPrototypeOf(ins) 或 ins.__proto__ 访问)指向构造函数的 prototype。
JavaScript实例化过程中原型链是怎么建立的
图2:实例化后内存空间中的指针映射,展示两个不同对象如何通过地址引用汇合到同一个原型对象

三、属性与方法沿链查找机制

当我们在实例上读取属性(例如 student.sayHello())时,JavaScript 引擎会启动属性检索过程:

  1. 检索自有属性:首先检查 student 对象自身内存块中是否存在 sayHello。如果存在,立即返回对应值并终止查找。
  2. 初次沿链跳转:如果自身没有,则顺着内部的 [[Prototype]] 指针跳转到其关联的原型 Person.prototype,在该对象中寻找。
  3. 继续向顶层追溯:如果在 Person.prototype 中仍未找到,继续读取该原型对象的 [[Prototype]],进入 Object.prototype。
  4. 终点终止:若在 Object.prototype 仍无结果,最终读取其原型指针。因为 Object.prototype.__proto__ === null,已到达链顶,引擎终止检索并返回 undefined(若作为方法调用则抛出 TypeError)。
JavaScript实例化过程中原型链是怎么建立的
图3:JavaScript 引擎在读取对象属性时的沿链查找路径逻辑

四、手写实现 new 操作符

验证对原型链建立过程理解的最佳方式是亲手实现一个模拟 new 行为的函数。以下代码完整复刻了引擎在底层执行的核心步骤:

function myNew(Constructor, ...args) {
  // 1. 创建一个新对象,并将原型指针链接到构造函数的 prototype
  // Object.create(proto) 的底层本质就是完成 obj.__proto__ = proto
  const instance = Object.create(Constructor.prototype);

  // 2. 将构造函数的 this 绑定为该新实例并执行
  const result = Constructor.apply(instance, args);

  // 3. 检查返回值:若构造函数显式返回引用类型对象,则返回该对象,否则返回 instance
  const isObject = typeof result === 'object' && result !== null;
  const isFunction = typeof result === 'function';

  return (isObject || isFunction) ? result : instance;
}

// 验证示例
function Person(name) {
  this.name = name;
}
Person.prototype.sayHello = function() {
  return `你好,我是 ${this.name}`;
};

const student = myNew(Person, '李华');
console.log(student.name);                  // "李华" (自有属性)
console.log(student.sayHello());            // "你好,我是 李华" (沿原型链调用)
console.log(student instanceof Person);     // true (原型链有效)
console.log(Object.getPrototypeOf(student) === Person.prototype); // true
注意事项: 在现代标准与生产实践中,应避免在代码中直接对 __proto__ 赋值。直接修改 __proto__ 会破坏 V8 等引擎的隐藏类(Hidden Class)及内联缓存(Inline Cache)优化,严重降低执行性能。标准推荐的做法是在对象创建时使用 Object.create(),或在必须读取/修改时使用 Object.getPrototypeOf() 与 Object.setPrototypeOf()。

五、ES6 Class 语法下的原型链建立机制

很多开发者会好奇,ES6 引入 class 关键字后,原型链的建立规则是否发生改变?答案是:核心原型链机制完全没有变化。ES6 class 本质上是基于现有原型机制的语法糖。

类中定义的构造器对应传统构造函数,在类主体内直接声明的普通方法依然会自动绑定到该类的 prototype 对象上。当我们通过 new MyClass() 实例化时,引擎依然执行上述四个关键步骤,将新实例的 [[Prototype]] 链接到 MyClass.prototype。

JavaScript实例化过程中原型链是怎么建立的
图4:JavaScript 实例化过程中原型链建立与属性查找的全局体系总结

六、核心要点总结

归纳起来,JavaScript 实例化过程中原型链的建立遵循以下核心规律:

  • 时机明确:原型链在 new 操作符执行的第 2 步完成,发生在构造函数内部赋值代码执行之前。
  • 核心动作单一:其实质操作就是一条简单的内部引用赋值:将新对象的内部隐藏属性 [[Prototype]] 指向构造函数的 prototype 对象。
  • 方法共享与节省内存:原型链的存在让所有同类实例都能直接调用原型上的方法,无需在每个实例创建时重复申请函数内存。
  • 单向回溯机制:属性查找是沿 实例 ➔ 构造函数.prototype ➔ Object.prototype ➔ null 单向向上的过程,若中途找到即刻返回,终点未找到则返回 undefined。
作者最新文章
相关文章
更多