类与继承

在 JavaScript 面向对象编程中,类(Class) 是创建对象的模板,而继承(Inheritance) 则允许子类复用父类的属性和方法并扩展新功能。ES6 引入 class 关键字规范化了类的定义,通过 extendssuper 实现继承,ES2022 进一步引入私有字段增强封装性,形成了完整的面向对象编程体系。

一、类的定义与基本结构

ES6 采用 class 关键字定义类,内部通过 constructor 方法初始化实例属性,并可直接定义实例方法。与传统构造函数模式相比,类语法更贴近面向对象的直觉表达。

基础类定义示例(以动物类 Animal 为例):

javascript
复制代码
class Animal {
  // 构造函数:初始化实例属性
  constructor(name) {
    this.name = name; // 公共属性:实例可直接访问
  }

  // 实例方法:定义动物发声行为
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

// 创建实例并调用方法
const animal = new Animal("Generic Animal");
animal.speak(); // 输出:"Generic Animal makes a noise."

类定义要点

  • constructor 是类的默认构造函数,实例化时自动执行,用于初始化 this 指向的实例属性。
  • 方法直接定义在类体中(无需 function 关键字),会被添加到类的原型对象上,供所有实例共享。

二、继承机制:extends 与 super

通过 extends 关键字可实现子类对父类的继承,子类通过 super() 调用父类构造函数,从而复用父类属性和方法,并可通过重写方法实现功能扩展。

动物类与子类案例(以 Dog 继承 Animal 为例):

javascript
复制代码
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

// 子类 Dog 继承父类 Animal
class Dog extends Animal {
  // 构造函数:通过 super 调用父类构造函数
  constructor(name, breed) {
    super(name); // 必须先调用 super(),再访问 this
    this.breed = breed; // 子类新增属性
  }

  // 方法重写:覆盖父类 speak 方法
  speak() {
    console.log(`${this.name} (${this.breed}) barks: Woof!`);
  }

  // 子类新增方法
  fetch() {
    console.log(`${this.name} fetches the ball.`);
  }
}

// 实例化子类并调用方法
const dog = new Dog("Buddy", "Golden Retriever");
dog.speak(); // 输出:"Buddy (Golden Retriever) barks: Woof!"
dog.fetch(); // 输出:"Buddy fetches the ball."

继承核心逻辑

  • extends 声明继承关系,子类原型链指向父类。
  • super() 在子类构造函数中必须优先调用,用于初始化父类属性;若子类未定义构造函数,会默认调用 super()
  • 方法重写通过在子类中定义同名方法实现,可通过 super.method() 在子类方法中调用父类方法(如 super.speak())。

三、私有字段与封装性

ES2022(ES13)引入私有类字段,通过 # 前缀声明,仅允许在类内部访问,有效隔离类的内部实现与外部接口,强化封装性。

计数器类(Counter)私有字段示例

javascript
复制代码
class Counter {
  #count = 0; // 私有字段:仅类内部可访问

  // 公共方法:操作私有字段
  increment() {
    this.#count++; // 类内部可访问私有字段
  }

  // 公共方法:读取私有字段
  getCount() {
    return this.#count;
  }

  // 静态方法:检查实例是否包含私有字段
  static hasPrivateField(instance) {
    return #count in instance; // 使用 #field in 语法检查
  }
}

// 使用计数器类
const counter = new Counter();
counter.increment();
console.log(counter.getCount()); // 输出:1
console.log(counter.#count); // 语法错误:私有字段无法从外部访问

// 检查私有字段
console.log(Counter.hasPrivateField(counter)); // 输出:true

私有字段特性

  • 访问限制:私有字段 #count 无法通过实例(如 counter.#count)或原型链访问,仅类内部方法可读写。
  • 封装优势:避免外部修改内部状态,确保类的行为可控(如 increment 方法可限制计数逻辑)。
  • 类型检查:通过 #field in instance 可判断对象是否为类的实例(比 instanceof 更严格,依赖私有字段标识)。

四、总结

ES6 类语法通过 classextendssuper 简化了面向对象编程的实现,而私有字段(#)的引入则补齐了 JavaScript 在封装性上的短板。通过类定义标准化继承机制清晰化内部状态私有化,JavaScript 面向对象编程更贴近传统面向对象语言的设计思想,同时保持了动态语言的灵活性。在实际开发中,合理使用类与继承可提升代码复用性,而私有字段则能有效保护类的内部实现,降低外部依赖风险。