__proto__ 指向实例的原型对象,prototype 是函数独有的属性;实例方法调用依赖原型链委托查找,如 p.sayHi() 会依次查找 p → Person.prototype → Object.prototype。

__proto__ 指向谁,prototype 在哪,决定了你调不调得到那个方法——这不是设计选择,是 JavaScript 运行时查属性的真实路径。
怎么一眼看出实例的原型是谁?
用 Object.getPrototypeOf(obj),别信 obj.__proto__ ——它不是标准 API,只是浏览器兼容实现。比如:
function Person(name) { this.name = name; }
const p = new Person('Alice');
console.log(Object.getPrototypeOf(p) === Person.prototype); // true
这个等式成立,才是你“连上原型”的铁证。如果手写对象字面量 {},它的原型就是 Object.prototype;如果是 [],原型是 Array.prototype;函数则是 Function.prototype。
为什么给 Person.prototype 加方法,p.sayHi() 就能用?
因为属性访问时 JS 会自动走这条链:p → p.__proto__(即 Person.prototype)→ Person.prototype.__proto__(即 Object.prototype)→ null。只要中间某一层有该属性,就停止查找。
- 如果在
p上定义了sayHi,就不会去原型里找 - 如果删掉
Person.prototype.sayHi,再调用p.sayHi()就是undefined - 如果误写成
Person.__proto__.sayHi,那是在找Function.prototype上的方法,和p无关
最容易踩的三个坑
第一,混淆 prototype 和 __proto__:只有函数才有 prototype;所有对象(除 null)才有 __proto__(或更推荐的 Object.getPrototypeOf())。
立即学习“Java免费学习笔记(深入)”;
第二,重写整个 prototype 后忘了修正 constructor:
Person.prototype = { sayHi() { } }; // ❌ 丢失 constructor
// 正确写法:
Person.prototype = { constructor: Person, sayHi() { } };
第三,给内置对象(如 Array.prototype)乱加方法——会影响所有数组,且可能和未来标准冲突,线上环境慎用。
原型链不是“继承”,是“委托查找”
别被“继承”这个词带偏:JS 里没有父类子类的概念,只有“我找不到,就问问我的原型”。所以修改 Person.prototype,所有已创建的 p、q 实例立刻生效;但如果你改的是 p.name,那只影响 p 自己。这种动态性是优势,也是调试时容易漏掉的点——你看到的方法,未必定义在对象自身。











