Set.Has()无法处理数组?如何用不可变类型存储坐标并高效查询
用不可变类型存储坐标适配Set的方案
原因分析
Set 判断元素是否存在采用 SameValueZero 算法:对于引用类型(比如数组),只有当两个值是同一个引用时才会被视为相等。所以即使 [1,2] 和另一个 [1,2] 内容完全一致,它们是不同的数组实例,Set.has() 会返回 false。
方案1:使用 TC39 实验性的 Tuple 类型
TC39 的 Tuple 提案定义了不可变的有序值集合,内容相同的元组会被视为相等(类似字符串、数字等基本类型的比较逻辑),完美适配 Set 的存在性检查。
用法示例
// 注意:目前属于实验性特性,需在支持的环境开启(比如 Chrome 启用 Experimental JavaScript 特性) const coordinateSet = new Set(); // 用 #[...] 创建元组 const coord1 = #[1, 2]; const coord2 = #[1, 2]; coordinateSet.add(coord1); console.log(coordinateSet.has(coord2)); // 输出 true
元组的核心特性:
- 不可变性:无法修改元组内的元素
- 值语义:内容相同则视为相等,而非引用相同
方案2:自定义不可变坐标类 + 享元模式
如果无法使用实验性特性,可以自定义一个不可变的坐标类,通过享元模式确保相同坐标复用同一个实例,让 Set 能正确识别重复元素。
实现示例
class ImmutableCoordinate { constructor(x, y) { // 锁定属性,确保不可修改 Object.defineProperty(this, 'x', { value: x, writable: false }); Object.defineProperty(this, 'y', { value: y, writable: false }); } // 静态缓存池,复用相同坐标的实例 static _cache = new Map(); static create(x, y) { const key = `${x},${y}`; if (!this._cache.has(key)) { this._cache.set(key, new ImmutableCoordinate(x, y)); } return this._cache.get(key); } } // 使用方式 const coordinateSet = new Set(); const coordA = ImmutableCoordinate.create(1, 2); const coordB = ImmutableCoordinate.create(1, 2); coordinateSet.add(coordA); console.log(coordinateSet.has(coordB)); // 输出 true
这种方式既保证了坐标的不可变性,又通过实例复用让 Set 能正确判断元素是否存在。
方案3:Object.freeze 冻结数组(局限性较大)
用 Object.freeze() 可以冻结数组使其不可变,但不同实例的引用依然不同,Set 会视为不同元素。只有始终复用同一个冻结数组实例时才生效:
const coord = Object.freeze([1, 2]); const coordinateSet = new Set([coord]); console.log(coordinateSet.has(coord)); // true console.log(coordinateSet.has(Object.freeze([1, 2]))); // false(不同引用)
这种方式仅适用于固定坐标的场景,不推荐动态创建坐标时使用。
内容的提问来源于stack exchange,提问作者Cincinnatus
相关产品推荐
相关产品推荐

