JavaScript原型机制基于构造函数、原型对象和实例三者通过prototype、__proto__和constructor属性绑定:构造函数的prototype指向原型对象,实例的__proto__指向该原型,原型的constructor回指构造函数,最终链向Object.prototype并以null终止。

JavaScript 中的 prototype 不是魔法,而是一套清晰、可追溯的关联规则——构造函数、原型对象、实例三者之间靠几个关键属性牢牢绑定,理解它们的关系,就等于握住了原型链的钥匙。
构造函数自带 prototype 属性
每个函数(包括你写的普通函数)在创建时,JS 引擎会自动为其添加一个 prototype 属性。这个属性指向一个默认生成的对象,就是“原型对象”。它不是构造函数自己拥有的数据,而是为将来用 new 创建的实例准备的共享模板。
-
function Person(name) { this.name = name; }定义后,Person.prototype就存在了,初始值是一个空对象,但带有一个constructor属性,指向Person函数本身。 - 你可以往
Person.prototype上添加方法或属性,比如Person.prototype.sayHi = function() { console.log('Hi!'); };,之后所有new Person()创建的实例都能直接调用sayHi()。 - 注意:
prototype是函数才有的属性,普通对象没有;而__proto__是对象才有的内部链接,不是标准 API,仅用于理解原理。
实例通过 __proto__ 指向原型对象
当你执行 const p = new Person('Alice');,JS 做了一件事:让新对象 p 的内部 [[Prototype]](可通过 p.__proto__ 访问)指向 Person.prototype。这就建立了继承关系。
- 这个链接是单向且固定的:实例 → 构造函数的
prototype。 -
p.__proto__ === Person.prototype永远为true,这是原型机制的核心恒等式。 - 访问
p.sayHi()时,JS 先查p自身有没有sayHi,没有就顺着__proto__找到Person.prototype,再在那里找到并执行。
constructor 属性维系回溯路径
原型对象上默认有个 constructor 属性,它的值就是对应的构造函数。这形成了一条可逆线索:从实例能向上找到原型,再从原型回到构造函数。
-
Person.prototype.constructor === Person是初始状态,确保“原型知道自己是谁造的”。 - 如果手动重写整个
prototype(比如Person.prototype = { sayHi() {} }),会丢失constructor,此时需显式补上:Person.prototype.constructor = Person;,否则instance.constructor就会指向Object。 - 这个属性不参与查找逻辑,纯属语义标记,但对调试、类型判断和某些框架(如 Vue 的组件识别)有实际用途。
原型链终点是 Object.prototype
所有自定义构造函数的原型对象,最终都连在 Object.prototype 上——它是绝大多数对象原型链的顶层。再往上,Object.prototype.__proto__ 是 null,链在此终止。
-
{}.toString()能用,是因为toString定义在Object.prototype上,所有对象(包括数组、函数)都继承它。 - 你给
Person.prototype添加方法,它不会影响Array.prototype或Date.prototype,因为它们各自指向不同的原型对象,只在自己的链上生效。 - 修改
Object.prototype是危险操作,会影响全局几乎所有对象,应严格避免。

















