重写valueOf与toString后对象全等比较异常的原因及解决方法
问题原因与解决方案
为什么p === s返回false?
- 数字
t1和t2是原始类型,全等运算符===比较原始类型时,直接对比值本身——只要值相同,结果就是true。 p和s是Box类的实例对象,属于引用类型。引用类型的===比较的是对象在内存中的地址:每执行一次new Box()都会在内存中新开辟一块空间创建对象,哪怕两个对象内部的#value相同,它们的内存地址也不一样,所以p === s返回false。- 你重写的
valueOf和toString方法,只有在需要类型转换时才会触发(比如把实例和原始值做运算、转成字符串时),但全等比较引用类型时不会触发这些方法,所以对===结果没有影响。
实现需求的方法:用缓存池复用实例
要实现“给原始值添加额外属性,且相同原始值的实例全等”,可以用**缓存池(单例模式)**的思路:每次创建实例时先检查缓存,已存在对应值的实例就直接返回,不存在再新建并存入缓存。
修改后的代码:
class Box { #value; // 静态私有缓存,存储已创建的实例 static #cache = new Map(); constructor(value) { this.#value = value; } // 用静态方法统一创建实例,代替直接new static of(value) { if (!Box.#cache.has(value)) { Box.#cache.set(value, new Box(value)); } return Box.#cache.get(value); } valueOf() { return this.#value; } toString() { return String(this.#value); } someMethod() { return "something else"; } } const t1 = 21; const t2 = 21; const p = Box.of(45); const s = Box.of(45); console.log(p === s, t1 === t2); // 输出 true true
代码说明
- 缓存机制:用静态私有属性
#cache(Map类型)存储原始值与对应实例的映射,确保相同原始值只会创建一个实例。 - 实例创建入口:通过静态方法
Box.of()创建实例,而不是直接new Box(),这样能统一控制实例的复用逻辑。 - 保留类型转换能力:
valueOf和toString依然生效,比如执行p + 5会得到50,String(p)会得到"45",和原始值的表现一致。
注意事项
- 如果需要处理大量不同的原始值,缓存会一直占用内存,可能导致内存泄漏,需要根据业务场景考虑是否添加缓存清理逻辑。
- 若
value是对象类型(比如Box.of({name: 'test'})),由于Map用对象作为键时比较的是引用地址,即使两个对象内容相同,也会被当成不同的键创建新实例。如果要支持对象内容相同复用实例,需要额外处理(比如将对象序列化为字符串作为键),但可能带来性能或冲突问题,需谨慎使用。
内容的提问来源于stack exchange,提问作者Alen.Toma
相关产品推荐
相关产品推荐

