javascript属性查找不报错而返回undefined,是因为引擎沿原型链逐级查找,查到object.prototype上命中tostring方法,查尽至null仍未找到nonexist则返回undefined;错误仅发生在执行undefined值时。

属性查找时,为什么 obj.toString 不报错但 obj.nonExist 返回 undefined
因为 JS 的属性查找是“有结果才停,没结果就一直往上找”,直到 Object.prototype.__proto__ === null 才终止。查到方法(如 toString)会在 Object.prototype 上命中,所以能执行;查不到的属性(如 nonExist)走到 null 仍无匹配,最终返回 undefined。
关键不是“找不到就报错”,而是“查到就用,查尽才认输”。这和方法调用失败时抛 TypeError 是两回事——错误发生在执行阶段,不是查找阶段。
-
obj.noExistMethod()报错,是因为查找返回了undefined,再对它加()就触发“Cannot read property 'call' of undefined” -
obj.noExistProperty不报错,只是安静地返回undefined - 所有普通对象的终点都是
Object.prototype,它的__proto__是null,这是硬编码的终止条件
__proto__ 和 prototype 混用会导致什么查找失效
直接读写 __proto__ 虽然可行,但会破坏原型链的可预测性。比如手动改掉某个实例的 __proto__,它就不再指向构造函数的 prototype,后续对该实例的属性查找将跳过原本应共享的方法。
更稳妥的方式是用 Object.getPrototypeOf(obj) 读、Object.setPrototypeOf(obj, proto) 写,它们是标准 API,且部分环境对 __proto__ 有性能警告或严格模式限制。
- 函数对象的
prototype是给它的实例用的,不是给自己用的;函数自身的原型是Function.prototype -
Array.prototype的__proto__是Object.prototype,不是Function.prototype - 不要给
Object.prototype随意添加属性——会影响所有对象的for...in和查找行为
为什么修改 Person.prototype 会影响已有实例
因为实例的 __proto__ 指向的是 Person.prototype 这个对象本身,不是它的快照或副本。只要这个对象被修改,所有引用它的实例在下次查找时都会看到新值。
这种动态性是原型链的核心优势,也是陷阱来源:你无法“冻结”某次创建时的原型状态,除非显式切断链接(如用 Object.create(null) 创建无原型对象)。
- 新增方法:所有已有实例立刻可用
person1.sayHi() - 覆盖方法:已有实例调用的不再是旧实现
- 删除属性:如果删的是
Person.prototype.name,而实例自身没有name,访问就会变成undefined
如何验证一个对象的完整原型链是否以 null 结尾
最可靠的方式不是层层点 __proto__,而是用 Object.getPrototypeOf 配合循环判断:
let obj = {};
let current = obj;
while (current !== null) {
console.log(current === Object.prototype ? '→ Object.prototype' : '→ other');
current = Object.getPrototypeOf(current);
}
// 最后一次循环中 current 会变成 null,退出
注意:Object.getPrototypeOf(Object.prototype) 明确返回 null,这是语言规范强制要求的。任何非 null 的返回值都意味着该对象不是标准普通对象(比如用 Object.create(null) 创建的)。
容易忽略的一点:Function、Array、RegExp 等内置构造函数的实例,虽然起点不同,但最终都汇入 Object.prototype → null 这条主干——这是 JS 统一查找语义的基础,不能绕开。











