原型链是JavaScript中实现继承的唯一底层机制,通过对象的[[Prototype]]隐式链接到构造函数的prototype,形成属性查找路径,终点为Object.prototype,其[[Prototype]]为null。

JavaScript 中没有传统面向对象语言里的“类继承”,原型链是实现对象间属性和方法共享的唯一底层机制。
原型链是怎么形成的
每个对象内部都有一个隐式原型 [[Prototype]](可通过 __proto__ 访问,但不推荐直接使用),它指向该对象的构造函数的 prototype 对象。当访问一个对象上不存在的属性时,JS 引擎会沿着 [[Prototype]] 一层层向上查找,直到找到或抵达 null —— 这条查找路径就是原型链。
常见错误现象:obj.toString() 能用,但 obj 自身并没有定义 toString;这是因为 Object.prototype.toString 在原型链顶端。
- 所有普通对象的原型链终点都是
Object.prototype,它的__proto__是null -
Function、Array、Date等内置构造函数的prototype对象,本身也是普通对象,所以也继承自Object.prototype - 手动设置
obj.__proto__ = null会切断原型链,导致失去所有继承方法(如hasOwnProperty不可用)
如何用原型链实现继承
核心是让子类型实例的 [[Prototype]] 指向父类型的 prototype 对象。最直接的方式是修改子构造函数的 prototype:
立即学习“Java免费学习笔记(深入)”;
function Parent(name) { this.name = name; }
Parent.prototype.say = function() { return 'Hi'; };
function Child(name, age) { Parent.call(this, name); this.age = age; }
Child.prototype = Object.create(Parent.prototype); // 关键:建立原型链
Child.prototype.constructor = Child; // 修复 constructor 指向
注意点:
-
Object.create(Parent.prototype)比直接赋值Child.prototype = Parent.prototype安全,后者会让子类修改影响父类原型 - 必须手动恢复
constructor,否则new Child().__proto__.constructor会指向Parent - 不调用
Parent.call(this, ...)的话,子实例无法获得父类实例属性(如this.name)
为什么现代代码很少手写原型链继承
因为容易出错且可读性差:漏掉 constructor 修复、忘记 call 父构造函数、误用 __proto__ 导致性能问题,都是高频坑。
ES6 后标准做法是用 class + extends,它只是原型链的语法糖:
class Parent { constructor(name) { this.name = name; } }
class Child extends Parent { constructor(name, age) { super(name); this.age = age; } }
这背后仍是操作 Child.prototype.__proto__ = Parent.prototype,但引擎自动处理了构造函数修复、super 绑定等细节。
真正需要关注原型链的场景,通常是调试时看 instanceof 判断、理解 hasOwnProperty 和 in 的区别,或者封装低层工具函数(如深克隆需遍历原型链)。
原型链本身很简单,难的是在各种继承写法(寄生组合、ES6 class、Object.setPrototypeOf 动态修改)中保持原型关系清晰——一旦中间某环被意外覆盖或设为 null,继承就静默失效,而且很难一眼发现。











