【问题标题】:How can I stop JavaScript from passing a class by reference?如何阻止 JavaScript 通过引用传递类?
【发布时间】:2021-11-05 17:29:44
【问题描述】:

我在类的构造函数中有以下代码(为了问题而缩短):

constructor(effect: EffectInstance, names: string[], count?: number) {
    this.effect = effect; // instance of a class "Effect"
    let name; for (name of names) {
        this.custom.set(name, this.effect); // custom: Map
    }
}

EffectInstance是这个类的类型,是泛型的

当我在类的方法中更改this.effect.name 时,或者当我从映射this.custom 中获取效果并更改其名称时,两者都会更改。

据我所知,这是由于 JavaScript 对对象的引用传递行为,因为我 100% 确定我没有修改我不想修改的值。 (我希望能够重命名 custom 映射中的 Effect 实例,但保持 this.effect.name 不变)

我尝试使用 constructor() 中的参数重新实例化类,但这引发了一个新问题:我会丢失类型,而且我似乎无法弄清楚如何解决这个问题。这是我尝试过的:

(EffectInstance,供参考:<EffectInstance extends Effect>)

constructor(effect: EffectInstance, names: string[], count?: number) {
    this.effect = effect;
    let altEffect = effect instanceof PlayerEffect ? new PlayerEffect(effect.name, effect.ignoreRaces) : new Effect(effect.name);
    let name; for (name of names) {
        this.custom.set(name, altEffect);
    }
}

这样做,TS 在altEffect 上引发此错误:

TS2345: Argument of type 'Effect' is not assignable to parameter of type 'EffectInstance'.   'Effect' is assignable to the constraint of type 'EffectInstance', but 'EffectInstance' could be instantiated with a different subtype of constraint 'Effect'.

我需要停止传递引用行为或保留 EffectInstance 包含的类型。我该怎么做?

【问题讨论】:

  • 您无法更改按引用传递的行为 - 这是 JavaScript 工作方式不可或缺的一部分。正如您所注意到的,您传入的 effect 是唯一一个同时是 this.effect 和地图中的值的实例。
  • @Joe 我不想改变行为;我正在尝试解决它或阻止它。如果措辞有误,请见谅
  • 我会考虑如何将名称与效果分离,因为您想重用效果实例。您在循环中实例化的包装对象如何影响效果并允许名称覆盖?
  • @Joe 目标是让this.effect 成为保留其默认状态的效果的单独实例,而地图中的实例旨在单独修改

标签: javascript typescript pass-by-reference


【解决方案1】:

通过你的代码,this.custom 类型应该是 Effect。

constructor(effect: EffectInstance, names: string[], count?: number) {
    this.custom = new Map<string, Effect>(); // altEffect type could be PlayerEffect or Effect
    this.effect = effect;
    let altEffect = effect instanceof PlayerEffect ? new PlayerEffect(effect.name, effect.ignoreRaces) : new Effect(effect.name);
    let name; for (name of names) {
        this.custom.set(name, altEffect);
    }
}

【讨论】:

  • 这是行不通的,因为泛型 (EffectInstance) 对于将特定的 Effect 扩展类向下传递到 Map 是必需的,这样打字保持一致
  • 但是你的代码使得 altEffect 可能是一个 Effect,并不总是一个 PlayerEffect。
  • 由于 Effect 通过了EffectInstance extends Effect 的过滤器,我看不出问题出在哪里。 EffectInstance 保存传入泛型类的类型,因此虽然 effect 可以是 Effect 或 PlayerEffect,但它始终与整个类实例中的其余效果类型相同。你能再解释一下吗?
【解决方案2】:

大多数语言通过引用传递对象,因此这不是 javascript 独有的行为。

如果您想传递类实例的副本而不是原始实例,并将克隆发送到您不想发送原始实例的地方。

class Car {
  constructor(name) {
    this.name = name;
  }
}

let orignalClass = new Car('BMW');

let cloneClass = Object.assign(Object.create(Object.getPrototypeOf(orignalClass)), orignalClass)

console.log(orignalClass);
console.log(cloneClass);

// now both can be updated individually
orignalClass.name = 'BMw-1';
cloneClass.name = "BMW-copy"

console.log("after update");
console.log(orignalClass);
console.log(cloneClass);

【讨论】:

  • 你不能像这样克隆类实例
  • 目标是保持类的实例化,并保留类型。 IIRC,不建议尝试 json-ify 类/函数
猜你喜欢
  • 2019-06-05
  • 2012-07-16
  • 1970-01-01
  • 1970-01-01
  • 2014-05-05
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多