属性遮蔽是指实例对象自身存在与原型同名属性时,访问该属性优先读取实例值,从而屏蔽原型属性;其发生机制是JavaScript严格按“先自身、再原型链”顺序查找,只要实例自有该属性(含undefined),就不会继续向上查找。

当实例对象上定义了和原型对象同名的属性时,访问该属性会优先读取实例自身的值,原型上的同名属性就被“遮住”了,这种现象就叫属性遮蔽。
遮蔽是怎么发生的
JavaScript 查找属性时严格遵循“先自身、再原型链”的顺序。只要实例自身有该属性(哪怕值是 undefined),就不会继续往上找原型。
- 比如
Person.prototype.name = 'Anonymous',而const p = new Person(); p.name = 'Alice',那么p.name就是'Alice',原型上的'Anonymous'不会被访问 - 即使把实例属性设为
undefined(p.name = undefined),它依然存在,遮蔽效果仍在 - 只有用
delete p.name真正移除该自有属性后,才会重新落到原型上
常见容易被遮蔽的场景
遮蔽不只发生在手动赋值时,很多看似自然的操作也会触发:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 构造函数里给
this.xxx = value,哪怕原型上已有同名方法或访问器,也会生成自有属性覆盖它 - 对一个原型上定义了
get/set的属性直接赋值(如obj.count = 5),若没锁死configurable: false,就会遮蔽访问器,后续再读写都变成操作自有数据 - 扩展原生对象(如给
Array.prototype.map赋新值)虽不常发生在实例层,但原理一致:一旦在实例上挂了同名属性,就切断了对原型方法的访问
如何避免意外遮蔽
关键不是禁止遮蔽,而是让遮蔽行为可控、可预期:
立即学习“Java免费学习笔记(深入)”;
- 在原型上定义方法或只读属性时,用
Object.defineProperty显式设置writable: false和configurable: false - 需要统一控制读写的场景,优先用
get/set描述符,并配合configurable: false防止被实例赋值覆盖 - 对整个原型对象调用
Object.freeze(Person.prototype),阻止新增、删除或重写任何原型属性 - 必要时用
Object.hasOwn(obj, 'prop')或obj.hasOwnProperty('prop')明确区分属性来源
遮蔽本身是语言机制的一部分,不是 bug。理解它发生的位置和条件,才能写出稳定、可维护的原型结构。

















