原型链查找在属性不在对象自身时触发,按自有属性判断,逐级向上查找至null;遮蔽会终止查找,终点恒为object.prototype→null。

原型链查找机制在你写 obj.prop 或调用 obj.method() 的那一刻就自动触发——只要这个属性或方法不在对象自身上,JavaScript 就会立刻开始沿 __proto__ 向上查找。
触发条件:属性不在对象自身上
查找不会无条件启动。只有当访问的属性名在对象自身中查不到时,引擎才启动原型链遍历。判断“是否在自身上”的依据是:自有属性(own property),即通过 Object.prototype.hasOwnProperty.call(obj, 'prop') 返回 true 的属性。
- 对象字面量直接写的属性、
obj.x = 1赋值的属性、Object.defineProperty添加的属性(默认enumerable: true或false),都算自有属性 - 构造函数内部用
this.name = 'xxx'设置的,也是实例自身的属性 - 而定义在
Constructor.prototype上的方法或数据,比如Array.prototype.push,不属于数组实例自身,访问时必然触发查找
属性访问四步流程(从 obj 到 null)
每次访问 obj.xxx,引擎严格按以下顺序执行:
- 检查
obj自身是否有xxx(含不可枚举属性) - 没有?取
Object.getPrototypeOf(obj)(推荐写法,比obj.__proto__更规范),去那个对象上找 - 还没找到?再对上一步结果重复调用
Object.getPrototypeOf,继续向上跳转 - 直到某一级有该属性,或到达
Object.prototype的上一级——也就是null,此时返回undefined
遮蔽会提前终止查找
如果实例上定义了和原型同名的属性,比如 person.name = 'Alice',而 Person.prototype.name = 'Unknown',那么访问 person.name 就永远停在实例层,不再往上走。这不是“跳过”,而是查找逻辑自然结束——因为已经找到了。
- 遮蔽只影响读取;写操作默认总是在实例上新建或修改属性(除非属性是 setter 且显式委托)
- 用
delete person.name可移除遮蔽,之后再次访问就会恢复原型链查找 - 注意:
hasOwnProperty只检测自有属性,不反映原型链上的存在与否
常见终点与默认路径
几乎所有普通对象的原型链终点都是 Object.prototype,它的 __proto__ 是 null。例如:
-
{}→Object.prototype→null -
[]→Array.prototype→Object.prototype→null -
/reg/→RegExp.prototype→Object.prototype→null - 自定义构造函数实例 →
Constructor.prototype→Object.prototype→null
这个路径不是约定,而是 JavaScript 引擎内置行为。你无法绕过 Object.prototype,也不能让普通对象的原型链指向 undefined 或其他非对象值。











