JavaScript原型链的核心是“谁指向谁”的创建逻辑:函数定义时自动拥有prototype属性;new调用时实例的[[Prototype]]指向该prototype;prototype自身又指向Object.prototype,最终以null终止;属性查找沿此链逐级向上。

继承中的原型链指向,不是靠记忆几个等式就能掌握的,关键在于理解“谁指向谁”背后的创建逻辑和查找路径。它决定了属性从哪来、方法怎么复用、instanceof为何成立,甚至影响框架源码的阅读能力。
构造函数、实例、原型三者的真实指向关系
这三者不是并列概念,而是有明确的创建顺序和单向依赖:
- 函数被定义时,自动拥有
prototype属性(指向一个空对象,含constructor) - 用
new Fn()创建实例时,该实例的内部[[Prototype]](可通过__proto__访问)被设为Fn.prototype -
Fn.prototype自身也有[[Prototype]],默认指向Object.prototype;而Object.prototype.__proto__是null,链在此终止
原型链上的查找过程:一次属性访问到底发生了什么
当你写 obj.method(),JS 引擎不是直接去 obj 上找,而是启动一套自动向上追溯的机制:
- 先查
obj自身是否有method(自有属性) - 没有?就看
obj.__proto__指向的对象(即构造函数的prototype)有没有 - 还没有?继续查
obj.__proto__.__proto__(比如Person.prototype.__proto__ === Object.prototype) - 直到查到
Object.prototype还没找到,就返回undefined;若中间某层有,立即返回并执行
继承实现中常见的指向“错位”与修正
手动实现继承时,最容易出问题的是 constructor 指针丢失或原型链断裂:
- 用
Child.prototype = new Parent()后,Child.prototype.constructor指向的是Parent,不是Child—— 必须手动修复:Child.prototype.constructor = Child - 如果直接赋值
Child.prototype = Parent.prototype,子类会污染父类原型,所有Parent实例也会获得子类方法 - 现代推荐方式是用
Object.setPrototypeOf(Child.prototype, Parent.prototype)或class extends,底层仍是设置__proto__指向,但更安全可控
函数本身也是对象:它的原型链长什么样
很多人只关注“实例的原型链”,却忽略函数自身的继承关系:
-
function Foo() {}是一个函数对象,所以它有__proto__ -
Foo.__proto__ === Function.prototype(因为函数由Function构造) -
Function.prototype.__proto__ === Object.prototype,再往上就是null - 这也解释了为什么函数能调用
.call()、.bind()等方法——它们定义在Function.prototype上

















