如何让对象/数组重赋值的变更在多类间可见以实现指针式行为
JavaScript中实现引用类型重赋值后的跨实例同步
原代码示例
class ClassA { constructor(positon, obj) { this.position = positon this.obj = obj } } class ClassB { constructor() { this.position = [1, 2] this.obj = { pos: [1, 2, 3], rot: [4, 5, 6] } this.classAInstance = new ClassA(this.position , this.obj) } } class ClassC { constructor(position, obj) { this.obj = obj this.position = position } reassignObj(){ this.obj = { pos: [4, 5, 6], rot: [7, 8, 9] } } resetObj() { this.obj.pos = [4, 5, 6], this.obj.rot = [7, 8, 9] } reassignPosition() { this.position = [3, 4] } resetPosition() { this.position[0] = 3 this.position[1] = 4 } } let fItem = new ClassB() let other = new ClassC(fItem.position, fItem.obj) other.resetObj() other.resetPosition() console.log('RE-SETTING') console.log('FITEM', fItem.position, fItem.obj) console.log('OTHER', other.position, other.obj) fItem = new ClassB() other = new ClassC(fItem.position, fItem.obj) other.reassignObj() other.reassignPosition() console.log('RE-ASSINGNING') console.log('FITEM', fItem.position, fItem.obj) console.log('OTHER', other.position, other.obj)
运行输出
//RESETTING FITEM [ 3, 4 ] { pos: [ 4, 5, 6 ], rot: [ 7, 8, 9 ] } OTHER [ 3, 4 ] { pos: [ 4, 5, 6 ], rot: [ 7, 8, 9 ] } // changes visible to other //REASSIGNING FITEM [ 1, 2 ] { pos: [ 1, 2, 3 ], rot: [ 4, 5, 6 ] } OTHER [ 3, 4 ] { pos: [ 4, 5, 6 ], rot: [ 7, 8, 9 ] } // changes not visible to other
问题核心
修改引用类型的内部属性时,所有持有该引用的实例都能同步看到变化;但直接重赋值整个对象/数组时,会让当前实例的引用指向新对象,原引用的实例无法感知到变化。要实现类似指针的跨实例同步,核心是保持共享的顶层引用不变,只修改其内部内容。
解决方案
1. 使用外层容器对象
把需要共享的数组/对象包装到一个外层容器中,所有实例引用这个容器。重赋值时,修改容器的属性而非替换容器本身,这样所有实例的容器引用不变,自然能同步看到新值。
修改后的代码示例:
class ClassA { constructor(positon, obj) { this.position = positon this.obj = obj } } class ClassB { constructor() { // 用容器包装需要共享的属性 this.sharedContainer = { position: [1, 2], obj: { pos: [1, 2, 3], rot: [4, 5, 6] } } this.classAInstance = new ClassA( this.sharedContainer.position, this.sharedContainer.obj ) } } class ClassC { constructor(sharedContainer) { this.sharedContainer = sharedContainer } reassignObj(){ // 修改容器的obj属性,而非替换当前实例的obj引用 this.sharedContainer.obj = { pos: [4, 5, 6], rot: [7, 8, 9] } } reassignPosition() { // 修改容器的position属性,而非替换当前实例的position引用 this.sharedContainer.position = [3, 4] } } let fItem = new ClassB() let other = new ClassC(fItem.sharedContainer) other.reassignObj() other.reassignPosition() console.log('RE-ASSINGNING WITH CONTAINER') console.log('FITEM', fItem.sharedContainer.position, fItem.sharedContainer.obj) console.log('OTHER', other.sharedContainer.position, other.sharedContainer.obj)
运行输出:
RE-ASSINGNING WITH CONTAINER FITEM [ 3, 4 ] { pos: [ 4, 5, 6 ], rot: [ 7, 8, 9 ] } OTHER [ 3, 4 ] { pos: [ 4, 5, 6 ], rot: [ 7, 8, 9 ] }
2. 集中式共享状态
创建一个全局或单例的共享状态对象,所有实例直接引用这个状态对象的属性。修改时直接操作状态对象的属性,所有实例都会同步感知到变化。
代码示例:
// 全局共享状态 const GlobalSharedState = { position: [1, 2], obj: { pos: [1, 2, 3], rot: [4, 5, 6] } } class ClassA { constructor() { this.position = GlobalSharedState.position this.obj = GlobalSharedState.obj } } class ClassB { constructor() { this.classAInstance = new ClassA() } } class ClassC { reassignObj(){ GlobalSharedState.obj = { pos: [4, 5, 6], rot: [7, 8, 9] } } reassignPosition() { GlobalSharedState.position = [3, 4] } } let fItem = new ClassB() let other = new ClassC() other.reassignObj() other.reassignPosition() console.log('RE-ASSINGNING WITH GLOBAL STATE') console.log('FITEM', fItem.classAInstance.position, fItem.classAInstance.obj) console.log('OTHER', GlobalSharedState.position, GlobalSharedState.obj)
3. 使用Proxy拦截赋值(进阶)
通过Proxy创建一个代理对象,拦截赋值操作,自动将新值同步到共享的目标引用上。这种方式更灵活,适合需要监听变化的场景。
代码示例:
function createSharedProxy(target) { return new Proxy(target, { set(target, prop, value) { // 直接修改原始目标的属性 target[prop] = value return true } }) } class ClassB { constructor() { this.shared = { position: [1, 2], obj: { pos: [1, 2, 3], rot: [4, 5, 6] } } this.classAInstance = new ClassA( createSharedProxy(this.shared.position), createSharedProxy(this.shared.obj) ) } } class ClassC { constructor(shared) { this.position = createSharedProxy(shared.position) this.obj = createSharedProxy(shared.obj) } reassignObj(){ // 赋值操作会被Proxy拦截,同步修改原始shared.obj Object.assign(this.obj, { pos: [4,5,6], rot: [7,8,9] }) } reassignPosition() { // 直接修改数组元素,或用Object.assign替换数组(数组是特殊对象) Object.assign(this.position, [3,4]) } }
内容的提问来源于stack exchange,提问作者rustyBucketBay
相关产品推荐
相关产品推荐

