__proto__ 是对象的内部 [[prototype]] 的可访问接口,指向其直接原型;原型链调试器则静态展开该链各层属性,二者共同揭示属性查找路径:对象→__proto__→__proto__→…→object.prototype→null。

__proto__ 属性和原型链调试器,本质上是在揭示 JavaScript 对象之间“查找属性时往哪找”这一核心行为。它们不是独立机制,而是对同一底层逻辑([[Prototype]] 内部链接 + 属性查找规则)的两种呈现方式:一个是可编程访问的接口,一个是开发者工具的可视化表达。
__proto__ 是什么,它怎么用
__proto__ 是对象的一个可读写、非标准但被广泛支持的属性,它是内部属性 [[Prototype]] 的暴露入口。它的值总是另一个对象(或 null),代表当前对象的“直接原型”。
- 它只存在于对象实例上(包括函数对象、数组、普通对象等),不是函数的属性;
- 构造函数本身有
prototype属性,而它的实例才有__proto__,且实例.__proto__ === 构造函数.prototype; - 修改
__proto__会动态改变该对象的原型链起点,影响后续属性查找(不推荐在生产环境频繁修改)。
常见用法示例:
function Animal() {}
Animal.prototype.speak = function() { console.log('sound'); };
const dog = new Animal();
console.log(dog.__proto__ === Animal.prototype); // true
console.log(dog.__proto__.__proto__ === Object.prototype); // true
console.log(dog.__proto__.__proto__.__proto__); // null(链终点)
原型链调试器如何还原查找过程
浏览器开发者工具(如 Chrome DevTools)中的“原型链”视图(通常在对象展开后的 [[Prototype]] 或 __proto__ 节点下),并不是实时运行代码,而是静态快照式地递归读取每个对象的 __proto__ 值,并逐层展开其自有属性。
它实际做了三件事:
- 从目标对象开始,读取其
__proto__指向的对象; - 对该原型对象,再读取它的
__proto__,直到遇到null; - 在每一层,列出该对象自身的可枚举/不可枚举属性(含方法),并标注来源(例如
from Animal.prototype); - 同时高亮显示哪些属性是“继承来的”,哪些是对象自己拥有的(对比
hasOwnProperty()结果)。
注意:
- 它不会执行 getter/setter,也不会触发 Proxy 的 trap;
- 如果某层原型是动态生成的(比如通过
Object.setPrototypeOf()或赋值__proto__),调试器能反映最新状态; - 但若原型链中存在
Object.create(null)创建的对象,其__proto__为undefined,链在此终止,调试器会明确标出“no prototype”。
如何配合使用定位问题
当你遇到 obj.method is not a function 或属性意外覆盖时,调试器 + __proto__ 检查非常高效:
- 先在控制台打印
obj.__proto__看第一层原型是否符合预期; - 展开
obj.__proto__.__proto__,确认是否连到了Object.prototype; - 在 DevTools 中右键对象 → “Store as global variable”(如
temp1),再执行temp1.hasOwnProperty('x')和'x' in temp1对比,判断属性归属; - 若发现某个方法出现在
Dog.prototype却调用报错,检查dog.__proto__是否仍指向Dog.prototype(避免被意外重置)。
为什么不能只靠 __proto__ 判断继承关系
因为 __proto__ 只体现运行时链接,不反映构造意图:
-
Object.create(Animal.prototype)创建的对象,__proto__指向Animal.prototype,但它没有constructor或new Animal()的初始化过程; - 箭头函数没有
prototype,所以不能做构造函数,它的实例(如果有)也无__proto__继承路径; - 类语法(
class)下的extends会自动设置Child.prototype.__proto__ === Parent.prototype,但你不能仅凭__proto__推断是否用了extends,还得看constructor链和super()调用痕迹。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











