JavaScript纯函数实现不修改原状态时如何保留正确引用关系?
纯函数实现下保留引用关联的解决方案
问题根因
非纯函数版本直接修改原始对象,result数组和原始ps数组的元素指向同一个引用,result[0].y[0]存储的ps[1]后续被修改时,访问的是同一个内存地址的对象,因此两次打印相等。
纯函数版本中incrX返回的是浅拷贝生成的新对象,result[1]是新生成的实例,而存入result[0].y的是未修改的原始ps[1]引用,两者指向不同的内存地址,因此打印结果不一致。
你提到的“引用的引用”思路在JavaScript中是可行的,JS没有原生二级指针语法,但可以通过间接容器实现同类效果,也可以通过调整操作顺序实现需求:
方案1:使用ref间接容器实现“引用的引用”
通过一层固定引用的容器包裹实际对象,所有关联存储都指向容器,更新时只替换容器内部的实际对象,容器本身的引用永远保持不变,就能保证关联引用的正确性。
示例代码:
class P { constructor() { this.x = 0 this.y = [] } } // 纯函数逻辑保持不变 const incrX = (p) => ({ ...p, x: p.x + 1 }) const incrY = (p, q) => ({ ...p, y: [...p.y, q] }) // 所有实例包裹在ref容器中,容器引用固定 const ps = [ { value: new P() }, { value: new P() } ] const result = ps.map((_, i) => { if (i === 0) { // 存储的是ref容器的固定引用 return { ...ps[i], value: incrY(ps[i].value, ps[1]) } } else { return { ...ps[i], value: incrX(ps[i].value) } } }) // 访问时取容器内部的实际对象 console.log(result[0].value.y[0].value === result[1].value) // 输出 true
方案2:调整操作顺序,先更新再关联
如果不想引入额外的容器层,也可以先完成所有对象的更新操作,再处理对象之间的关联逻辑,保证存入的是更新后的新对象引用。
示例代码:
class P { constructor() { this.x = 0 this.y = [] } } const incrX = (p) => ({ ...p, x: p.x + 1 }) const incrY = (p, q) => ({ ...p, y: [...p.y, q] }) const ps = [new P(), new P()] // 第一步先完成所有实例的更新操作 const tempResult = ps.map(p => incrX(p)) // 第二步再处理关联逻辑,传入更新后的实例引用 const result = tempResult.map((p, i) => i === 0 ? incrY(p, tempResult[1]) : p) console.log(result[0].y[0] === result[1]) // 输出 true
方案选型
- ref容器方案适合对象依赖关系复杂、互相引用多的场景,不需要调整操作顺序,缺点是多了一层属性访问。
- 调整顺序方案适合依赖关系清晰的简单场景,代码更直观无额外开销。
内容的提问来源于stack exchange,提问作者user7311437
相关产品推荐
相关产品推荐

