为何自定义Variable类实例的++运算符类型转换失效,而+运算正常?
为何自定义Variable类实例的++运算符类型转换失效,而+运算正常?
先看你提供的示例代码:
class Variable { #value; #dependents = new Set(); constructor(value) { this.value = value; } get value() { return this.#value; } valueOf() { return this.value; } toString() { return String(this.value); } toNumber() { return Number(this.value); } set value(value) { if (this.#value === value) return; this.#value = value; var change = new WeakSet(); for (const dep of this.#dependents) dep.update(change); } register(dependent) { if (!(dependent instanceof Dependent)) throw new TypeError("Not a Dependent"); this.#dependents.add(dependent); } } class Dependent extends Variable { constructor(variables, calculation) { super(); this.variables = variables; this.calculation = calculation; for (const variable of variables) { if (!(variable instanceof Variable)) throw new TypeError("Not a Variable"); variable.register(this); } this.calculate(); } update(change) { if (change.has(this)) throw new Error("Cyclic dependency"); change.add(this); this.calculate(); } calculate() { this.value = this.calculation(...this.variables); } } var a = new Variable(1); var b = new Variable(1); var c = new Dependent([a, b], (a, b) => a + b); console.log(`${a} + ${b} = ${c}`); a.value++; console.log(`${a} + ${b} = ${c}`); a++; console.log(`${a} + ${b} = ${c}`);
这问题其实是JS不同运算符的底层行为差异导致的,咱们一步步拆解:
为什么a + b能正常工作?
当你使用+运算符处理对象类型的操作数时,JS引擎会自动触发ToPrimitive抽象操作,把对象转成原始值。你的Variable类正好实现了valueOf()方法,它会返回实例内部的#value数字,所以a + b会被转成1 + 1,完全符合预期。而且这个过程不会修改变量a或b本身,它们始终是Variable实例,依赖关系也保持完整。
为什么a++会“失效”?
这得搞懂++前缀运算符的完整执行逻辑:
- 第一步:把操作数(
Variable实例a)转成数字——同样会调用valueOf()得到当前值1,加1后得到2; - 第二步(也是关键):JS引擎会把这个运算后的数字
2直接赋值给变量a。也就是说,原来的Variable实例被彻底覆盖了!现在变量a不再是Variable对象,而是一个普通的数字2。
这就导致后续代码里,a已经和之前的Variable实例没有任何关系了,自然也不会触发Dependent的更新,看起来就像是“类型转换失效”,但本质是变量的引用被替换了。
正确的写法是什么?
JS里的赋值运算符(包括++、--这类隐含赋值的运算符)是无法重载的,所以你没办法让a++直接修改实例内部的#value。如果你想更新Variable实例的值同时触发依赖更新,必须显式修改它的value属性:
a.value++; // 这才是正确的写法
备注:内容来源于stack exchange,提问作者ceving
相关产品推荐
相关产品推荐

