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

如何实现JavaScript类指定方法不可重写,其余方法可重写?

如何在JavaScript中让类的指定方法不可重写,同时保留其他方法的可重写能力?

问题描述

我们需要实现这样的类继承逻辑:

  • 基类Base包含两个方法:talk和walk
  • 子类Derived继承Base后,不能重写talk方法(子类定义同名方法应报错或不生效,调用时仍执行基类逻辑)
  • 子类Derived可以正常重写walk方法,调用时执行子类逻辑

期望的伪代码示例:

class Base
{
    final talk()
    {
        console.log("I am a talking class!");
    }

    walk()
    {
        console.log("I am walking!");
    }
}

class Derived extends Base
{
    talk() // 此处应报错
    {
        console.log("I don't talk!");
    }

    walk() // 此处正常允许
    {
        console.log("I am not walking!");
    }
}

let der = new Derived();

der.talk(); // 应输出 "I am a talking class!"
der.walk(); // 应输出 "I am not walking!"

背景参考

在C++中可以通过final关键字直接实现这个需求,示例代码如下:

#include <iostream>

class Base
{
public:
  virtual void Start () final
  {
    std::cout << "Starting!";
  }
  
  virtual void End()
  {
    std::cout << "Ending!";
  }
};

class Derived : Base
{
public:
  void Start ()
  {
    std::cout << "Not starting!";
  }
  
  void End()
  {
    std::cout << "Not ending!";
  }
};

int
main ()
{
  Derived *newDerived= new Derived();
  newDerived->End();

  return 0;
}

编译时会直接报错,提示子类重写了被标记为final的方法:

main.cpp:13:8: error: virtual function ‘virtual void Derived::Start()’ overriding final function
   13 |   void Start ()
      |        ^~~~~
main.cpp:5:16: note: overridden function is ‘virtual void Base::Start()’
    5 |   virtual void Start () final
      |          

但在TypeScript中没有找到对应实现,JavaScript原生也不支持类方法的final修饰符。此前尝试用Object.defineProperty()的方案,只能让方法在实例上不可变,但子类仍然可以通过定义同名方法遮蔽基类方法,无法达到预期效果。

解决方案

以下是可行的JavaScript实现代码,通过实例属性锁定+私有辅助方法的方式,实现指定方法不可重写,同时保留其他方法的可重写能力:

"use strict";

class Base {
    #name;

    #makeMethodsFinal(methods) {
        for (const [methodName, method] of Object.entries(methods)) {
            Object.defineProperty(this, methodName, {
                writable: false,
                configurable: true,
                value: method.bind(this)
            });
        }
    }
    
    leave() {
      console.log("Bye!");
    }

    constructor(name) {
        this.#name = name;
        
        const finalMethods = {
            present: () => { console.log(`Hello! My name is ${this.#name}`); }
        };

        this.#makeMethodsFinal(finalMethods);
    }
}

class Derived extends Base {
    // 尝试重写final方法,严格模式下会报错
    present() {
        console.log("This won't be printed");
    }

    // 正常重写非final方法
    leave() {
        console.log("It was nice seeing you!");
    }
}


let der = new Derived("Franco");

der.present(); // 输出 "Hello! My name is Franco"
der.leave();   // 输出 "It was nice seeing you!"

实现原理

  1. 在基类的构造函数中,将需要标记为“不可重写”的方法定义为实例属性,而非原型方法
  2. 通过Object.defineProperty()将这些实例属性的writable设置为false,严格模式下子类尝试赋值该属性会直接报错,非严格模式下赋值会静默失败
  3. 利用箭头函数或bind绑定this,确保方法能正常访问基类的私有属性
  4. 其他未被标记的方法(如leave)仍然是原型方法,子类可以正常重写

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 11:42:34