1. 类的基本概念与核心特性类Class作为面向对象编程OOP的核心构造本质上是一种自定义数据类型。它通过封装数据属性和行为方法来描述现实世界中的实体。在JavaScript ES6之前开发者需要通过函数和原型链模拟类的行为而ES6的class语法糖让面向对象编程变得更加直观。类的三个基本特性封装将数据和操作数据的方法绑定在一起继承子类可以复用父类的特性多态同一操作作用于不同对象产生不同行为class Person { constructor(name) { this.name name; // 实例属性 } greet() { // 实例方法 console.log(Hello, ${this.name}!); } }注意class本质仍是基于原型的语法糖typeof Person 返回function2. 类的完整语法结构解析2.1 构造方法constructorconstructor是类的特殊方法在通过new创建实例时自动调用。一个类只能有一个constructor省略时会自动添加空constructor。class Rectangle { constructor(height, width) { this.height height; this.width width; this.area height * width; // 可直接计算派生属性 } }构造函数的典型用途初始化实例属性执行必要的设置逻辑参数验证和默认值处理2.2 实例方法与属性定义在类中的方法默认添加到原型上所有实例共享class Circle { constructor(radius) { this.radius radius; } // 实例方法 getArea() { return Math.PI * this.radius ** 2; } // Getter方法 get diameter() { return this.radius * 2; } // Setter方法 set diameter(value) { this.radius value / 2; } }2.3 静态方法与属性使用static关键字定义只能通过类本身访问class MathUtils { static PI 3.14159; // 静态属性 static max(...values) { // 静态方法 return Math.max(...values); } } console.log(MathUtils.max(1, 5, 3)); // 5静态成员的常见使用场景工具函数工厂方法不需要实例化的常量2.4 私有字段ES2022使用#前缀定义私有字段只能在类内部访问class BankAccount { #balance 0; // 私有字段 deposit(amount) { this.#balance amount; } get balance() { return this.#balance; } }3. 类的继承机制详解3.1 基础继承语法使用extends实现继承super关键字用于访问父类class Animal { constructor(name) { this.name name; } speak() { console.log(${this.name} makes a noise.); } } class Dog extends Animal { constructor(name, breed) { super(name); // 必须首先调用super() this.breed breed; } speak() { // 方法重写 console.log(${this.name} barks!); } }3.2 super关键字的三种用法super()在子类constructor中调用父类构造函数super.method()调用父类方法super.property访问父类属性较少用class Cat extends Animal { speak() { super.speak(); // 先调用父类方法 console.log(${this.name} meows!); } }3.3 继承内置类型类可以继承Array、Error等内置类型class MyArray extends Array { get first() { return this[0]; } get last() { return this[this.length - 1]; } } const arr new MyArray(1, 2, 3); console.log(arr.first); // 14. 类的进阶特性与模式4.1 混入模式MixinsJavaScript支持通过函数实现混入const Flyable (Base) class extends Base { fly() { console.log(${this.name} is flying!); } }; class Bird extends Flyable(Animal) { // 现在拥有fly方法 }4.2 抽象基类通过new.target可以创建抽象基类class Abstract { constructor() { if (new.target Abstract) { throw new Error(Cannot instantiate abstract class); } } }4.3 类表达式类也可以使用表达式形式定义const Person class { constructor(name) { this.name name; } };5. 类与原型的关系揭秘虽然class语法更简洁但底层仍基于原型链class User {} console.log(typeof User); // function // 等价于 function User() {} User.prototype.constructor User;关键区别类方法不可枚举类必须用new调用类声明不会提升类内部默认为严格模式6. 实战中的最佳实践6.1 属性初始化技巧现代JavaScript支持直接在类中声明字段class Post { title Untitled; // 实例字段 views 0; // 带默认值 constructor(title) { if (title) this.title title; } }6.2 方法绑定的坑与解决方案类方法中的this默认指向实例但在回调中可能丢失class Logger { constructor() { this.log this.log.bind(this); // 手动绑定 // 或使用箭头函数 this.log (message) { console.log(message); }; } }6.3 私有方法的实现方式在ES2022之前常用以下模式实现私有class Widget { constructor() { this._internal {}; // 约定俗成的私有 } // 使用Symbol实现真正私有 #privateMethod () { // ... }; }7. 类与模块系统的配合类通常作为模块的默认导出// person.js export default class Person { // ... } // main.js import Person from ./person.js;也可以导出多个类export class Student { /*...*/ } export class Teacher { /*...*/ }8. 常见问题排查指南8.1 Must call super constructor错误当子类有constructor但未调用super()时发生class Child extends Parent { constructor() { // 忘记super() this.prop 123; // 报错 } }解决方案添加super调用完全省略constructor8.2 方法中的this丢失当类方法被单独提取使用时const { log } new Logger(); log(); // this为undefined修复方案构造函数中绑定this使用箭头函数方法调用时使用.call/.apply8.3 私有字段访问错误尝试从类外部访问#私有字段时class Example { #secret 123; } const e new Example(); console.log(e.#secret); // SyntaxError正确做法通过公共方法暴露必要数据使用getter控制访问9. 性能考量与优化原型方法优于实例方法将方法定义在原型上类语法默认行为比在每个实例上创建方法更高效避免过度继承深层次的继承链会影响性能谨慎使用getter/setter复杂的getter/setter可能成为性能瓶颈考虑对象池模式对频繁创建销毁的类实例使用对象池// 对象池示例 class Pool { static #pool []; static getInstance() { return this.#pool.pop() || new this(); } static release(instance) { this.#pool.push(instance); } }10. 现代JavaScript中的类演进ES2022正式支持类字段包括私有字段ES2023新增静态初始化块class Cache { static { // 类加载时执行 this.maxSize 100; } }装饰器提案未来可能支持类和方法装饰器observable class Store { memoize get data() { /*...*/ } }