You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在JavaScript中能否继承仅拥有私有构造函数的类?

问题背景与疑问

在多数编程语言中,继承拥有私有构造函数的类是不可行的——因为子类必须调用父类的构造函数(比如Java中,暂不考虑特殊反例)。

JavaScript本身没有原生支持私有构造函数,但存在模拟私有构造函数的常用模式。我原本以为这类模拟私有构造的类也会有相同限制:无法被继承。

HTMLVideoElement就是一个典型例子:直接调用new HTMLVideoElement()会抛出Illegal constructor异常(在Firefox 105、Chrome 106中测试验证),看起来完全符合“私有构造函数类”的特征。

但当我创建它的子类并注册为自定义元素后,情况发生了变化:

class MyVideo extends HTMLVideoElement {
    constructor() {
        super();
    }
}
customElements.define('my-video', MyVideo, { extends: "video"})

此时调用new MyVideo()不会抛出异常,可以正常实例化。

我既对这个可行的结果感到惊喜,又担心如果依赖该行为,未来浏览器是否会修改它——毕竟JS目前没有原生私有构造函数,相关行为的规范可能不够明确。

我的测试显示答案是“可以继承并实例化”,但理论上我觉得应该“不可以”。想请教:这是预期行为吗?未来能否安全依赖这个特性?

补充说明:只有先通过customElements.define注册子类后,new MyVideo()才不会抛异常;如果跳过注册直接调用构造函数,依然会抛出异常(已在Firefox和Chrome中验证)。


解答

这是符合规范的预期行为,而且未来可以安全依赖——因为这是自定义元素标准和原生元素构造机制共同规定的行为,并非浏览器的临时实现。

1. 为什么HTMLVideoElement不能直接实例化?

HTMLVideoElement这类原生元素的构造函数属于受限制的构造函数,并非传统意义上的“私有构造函数”。浏览器限制直接调用它的原因是:原生元素的实例化需要绑定DOM环境的上下文,直接new无法提供正确的初始化上下文(比如元素的文档关联、内部状态初始化等)。

2. 自定义元素注册为什么能解除这个限制?

当你通过customElements.define注册继承自原生元素的自定义类时,浏览器会为这个子类构造函数注入特殊的初始化逻辑:

  • 注册后,子类的super()调用不再直接调用原生HTMLVideoElement的构造函数,而是通过浏览器提供的内部接口完成原生元素的实例化,确保上下文正确。
  • 规范明确要求:对于自定义内置元素(即继承自原生HTML元素的自定义元素),注册后必须允许通过new实例化,同时保证实例符合原生元素的所有特性。

3. 和普通模拟私有构造函数的类的区别

普通JS模拟私有构造的模式(比如在构造函数里判断调用者,非允许的情况抛出错误),是开发者自己实现的逻辑,没有浏览器的特殊支持。而HTMLVideoElement的限制是浏览器层面的,自定义元素标准专门为继承这类原生元素的场景做了适配,所以两者的机制完全不同。

4. 关于未来兼容性

这个行为已经被纳入自定义元素v1规范,目前所有支持自定义元素的浏览器(Chrome、Firefox、Edge、Safari)都一致实现了该特性,而且规范没有计划修改这一逻辑。只要你遵循自定义元素的注册流程,这个行为就是稳定的,可以放心依赖。

内容的提问来源于stack exchange,提问作者Sebastian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 14:20:24