如何修改ES6类的构造函数

7
我正在尝试使用 ES6 类完成热代码重载。我需要能够修改类的构造函数,而不是用新的类替换它(因为其他人可能已经引用了它)。
但是,我发现类对象似乎具有一些内部引用指向最初定义的构造函数;使用 new 实例化类时,不会在类上查找 constructor 或 prototype.constructor。
例如:

class OldC { constructor() { console.log("old"); } }
class NewC { constructor() { console.log("new"); } }

OldC.prototype.constructor = NewC.prototype.constructor;
OldC.constructor = NewC.constructor;
new OldC();

---> "旧的"

(更新所有其他方法都可以正常工作;只有构造函数让我感到困扰。)

考虑到可能是通过[[prototype]]找到了构造函数,我还添加了以下内容:

Object.setPrototypeOf(OldC, Object.getPrototypeOf(NewC));
Object.setPrototypeOf(OldC.prototype, Object.getPrototypeOf(NewC.prototype));

这也没有帮助(考虑到没有子类化发生,我也不会指望它有所帮助)。

尽管经过了这么多步骤,检查OldC后发现原型属性正如我所预期的那样,其中OldC.prototype.constructor是新构造函数。但是,构造OldC的实例仍然调用原始构造函数。

到底发生了什么,我该如何解决?


抱歉,打错了。已修正。 - David Given
1
问题归结为:“我能改变一个函数吗?”请参见此答案 - trincot
在JS中,“类”并不是独立的东西。它只是用于创建构造函数的语法糖。因此,“类”的.constructor都是Function - ziggy wiggy
ES6类并不是严格意义上的函数,它们不能被调用,只能被构造。 - David Given
@DavidGiven:没错,它们仍然是函数。自 ES2015 以来,函数可以是可调用的、可构建的,或者两者都可以。 - Felix Kling
显示剩余2条评论
1个回答

4

然而,构造一个 OldC 实例会调用原始构造函数。

是的,因为 OldC 本身就是构造函数。您需要覆盖 OldC 变量以更改 new OldC 的行为。

修改类的构造函数,而不用替换整个类(因为其他人可能有对它的引用)。

正如 @trincot 在评论中指出的那样,您无法修改函数的代码。您必须用新的构造函数替换构造函数,没有其他方法。

但是,您可以保留原型对象(可变),因为这是大多数其他内容(特别是旧实例)将引用的对象。

NewC.prototype = OldC.prototype;
NewC.prototype.constructor = NewC;
OldC = NewC;

现在无法更改旧构造函数的引用的人无法得到帮助。你最好的选择不是直接分发该类本身,而是只提供一个工厂,让你的更新代码知道如何修改其行为。


不幸的是,那些引用旧类的人 必须 得到帮助,因为这是项目中不可谈判的要求。将其替换为工厂也不太可能奏效,因为 class Subclass extends superclassFactory() 将隐式引用 class Superclass,而超级类的构造函数也不会通过原型查找。通过扩展代理实例可能可以做一些非常恶心的事情;我需要进行实验。 - David Given
@DavidGiven,我不确定您如何进行热代码重新加载,但为了帮助那些引用旧构造函数的人,您可能需要调试器访问运行时以识别和更改所有这些引用。如果您想使用环境内部的代码来完成它,除了在初始代码中使用constructor(...args) { this.init(...args); }并替换init方法之外,您无能为力。 - Bergi
我实际上正在使用TypeScript,因此我可以安装自定义转换器来精确执行此操作。这可能不像滥用Proxy那样恶心。编辑:除非以这种方式我会失去所有有用的TypeScript构造函数扩展... - David Given
@DavidGiven 如果您可以将此集成到构建链中,那么这将是完美的解决方案。我建议不要将方法命名为 init,而是更像 Symbol.for("hotreload-actualconstructor") 这样的名称 :-) 或者甚至不使用方法,而只是调用一个本地变量,您的重新加载器可以访问和更改。 - Bergi
我做了這件事。基本上,我完全放棄了 ES6 類結構,回歸到一個類被作為函數構造器和原型鏈的傳統 JS 模型;構造函數僅在新對象上調用 __constructor。由於這是一個普通方法,我可以更新它。然後有一堆可怕的 TypeScript 轉換器,將 TypeScript 類聲明改寫成這個模型。到目前為止,它通過了我所有的測試,這讓我感到非常驚訝。 - David Given

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接