原型链是javascript属性查找的真实路径,从实例对象开始沿__proto__逐级向上查找,直至object.prototype,最终抵达null。

原型链不是语法糖,而是 JavaScript 属性查找的真实路径——它从实例对象开始,沿着 __proto__ 一级级往上找,直到 Object.prototype,最终抵达 null。理解它,关键不在记住概念,而在看清“谁找谁、怎么找、找什么”。
原型链的本质是属性查找链
当你写 obj.method(),JavaScript 并不假设 obj 自己有这个方法。它会:
- 先查
obj自身是否有method; - 没有?就查
obj.__proto__(即它的原型); - 还没找到?继续查
obj.__proto__.__proto__; - 一直查到
Object.prototype,它的__proto__是null,查找终止。
这条由 __proto__ 连接起来的路径,就是原型链。所有普通对象的终点都是 Object.prototype,这也是为什么几乎所有对象都能调用 toString() 或 hasOwnProperty() ——它们就定义在这里。
prototype 和 __proto__ 不是同一个东西,但它们必须对齐
prototype 是函数才有的属性,指向一个对象(即“将来实例的原型”);__proto__ 是每个对象都有的内部链接,指向它的原型对象。
继承成立的前提,是子类实例的 __proto__ 最终能连到父类的 prototype 上。例如:
-
function Person() {}→Person.prototype是未来new Person()实例的原型; -
function Student() {}→ 要让Student继承Person,就得让Student.prototype.__proto__ === Person.prototype; - 验证是否成功:用
Object.getPrototypeOf(new Student()) === Person.prototype,而不是靠有没有写extends。
别用 new 父类赋值 prototype,改用 Object.create()
常见错误:Student.prototype = new Person()。这会让父类构造函数立即执行一次——如果它依赖参数、发请求或修改状态,问题就来了。
正确做法是只建立原型连接,不触发构造逻辑:
-
Student.prototype = Object.create(Person.prototype)—— 创建一个空对象,其__proto__直接指向Person.prototype; - 补上构造器:
Student.prototype.constructor = Student,否则new Student()实例的constructor会指向Person; - 子类构造函数里仍需
Person.call(this, ...),因为原型链只传递方法,不传递this.name这类实例属性。
class 和 extends 只是封装,底层没变
class Student extends Person 看起来像 Java,但编译后仍是基于 Object.setPrototypeOf() 和 Person.call() 的组合。Babel 生成的 _inherits 辅助函数,就是在做这两件事:
- 设置
Student.prototype.__proto__ = Person.prototype; - 在子类
constructor中自动插入super(),本质就是Person.call(this); - 漏掉
super(),this就未初始化,运行时报错。
静态方法不进原型链,它们挂在函数本身上,所以 Student.staticMethod() 和 student.staticMethod() 完全是两回事。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











