JavaScript属性访问是运行时按需查找+懒绑定,先查实例自有属性,再沿原型链逐层向上直至null;写入默认作用于实例自身,不修改原型;可枚举性与访问性相互独立。
JavaScript 中属性的动态发现与绑定,本质上是运行时按需查找 + 懒绑定的过程,不预先声明、不静态编译,全靠原型链实时导航。
当你访问 obj.prop 时,引擎不会提前知道这个属性在哪。它严格按以下顺序行动:
obj 自身是否拥有该属性(自有属性,即 Object.hasOwnProperty('prop') 为 true)obj.__proto__(即 Object.getPrototypeOf(obj)),在该对象上查找__proto__,重复查找Object.prototype.__proto__ === null 终止,返回 undefined
这个过程完全动态,每次访问都走一遍链——不是缓存路径,而是实时“导航”。即使你在运行中修改了某层原型(比如给 Array.prototype 新增方法),后续所有数组实例立刻能访问到,因为查找逻辑始终生效。
JavaScript 中并不存在传统意义上的“属性绑定”。所谓“调用 obj.method()”,其实只是:
立即学习“Java免费学习笔记(深入)”;
method 函数(可能在原型上)obj 作为 this 上下文执行它函数本身并不“属于” obj;它只是被查找到、被调用。你可以把同一个函数赋给多个对象,也可以临时替换某个实例的属性覆盖原型上的同名方法——所有这些操作都不影响原型链结构,只改变某次查找的结果。
当你执行 obj.prop = value,引擎默认在 obj 自身上创建或更新属性,不会顺着原型链向上写。这是关键设计:
obj 的原型上有同名 prop,赋值也只作用于 obj 自身(屏蔽效应)Constructor.prototype.prop = value
last 方法要写 Array.prototype.last = function(){...},而不是 [].last = ...
属性能否被 for...in 或 Object.keys() 看到,取决于它的 enumerable 描述符,而非位置。例如:
Object.prototype.toString 存在于原型链上,但 enumerable: false,所以遍历时不可见Object.defineProperty 在原型上添加非枚举属性,它依然能被点号访问,但不会出现在循环中也就是说,“能访问”和“能枚举”是两套机制——查找走原型链,遍历看描述符。