遮蔽效应是JavaScript原型继承中属性查找的就近覆盖行为:对象自身属性会终止查找,遮蔽原型同名属性;它不直接增加内存,但导致方法重复定义、失去共享优势,并阻碍引擎优化。

原型继承中的“遮蔽效应”(Shadowing)不是Java那种编译期变量遮蔽,而是JavaScript中属性查找时的**就近覆盖行为**:当一个对象自身拥有某个属性,它就会“遮蔽”原型链上同名的属性,使查找在该层终止,不再继续向上追溯。
遮蔽如何影响内存空间
遮蔽本身不额外占用显著内存,但它会间接导致内存使用不合理:
- 每个实例重复定义本可共享的属性(比如把方法或常量写在构造函数里而非 prototype),造成多份副本驻留堆内存
- 若子类构造函数中用 this.xxx = xxx 覆盖了父类原型上的同名属性,就丢失了原型复用优势,相当于把“共享资源”降级为“私有拷贝”
- 过度遮蔽还可能阻碍V8等引擎的隐藏类(Hidden Class)优化,使对象无法进入快速属性访问路径
遮蔽对执行效率的拖累
关键不在遮蔽动作本身,而在它引发的低效模式:
- 属性查找变短了(看似更快),但若因遮蔽导致大量重复数据,GC压力上升,长期反而降低吞吐
- 每次访问被遮蔽的原型方法时,引擎仍需先检查实例自身是否有该属性——这个检查开销虽小,但在高频调用场景(如动画帧、事件循环)中会被放大
- 最典型的是把方法写在构造函数内:function Person() { this.sayHi = () => console.log('hi'); },每个 new Person() 都新建一个函数对象,而放在 Person.prototype.sayHi 上则全实例共享一个函数
如何识别和规避有害遮蔽
判断是否属于“坏遮蔽”,看三点:
- 是否用实例属性覆盖了本应统一管理的状态(如配置项、工具方法)
- 是否在构造函数中重复赋值原型上已存在的属性(尤其是函数或大型对象)
- 是否因遮蔽导致 instanceof 或 isPrototypeOf 判断失真(例如手动设置 constructor 属性却未修正原型链)
推荐做法:优先把方法、只读配置、通用行为放 prototype;仅将真正属于单个实例的、可变的数据放 this 上;必要时用 Object.defineProperty 控制可枚举性与可写性,避免意外遮蔽。

















