JavaScript 类(class) constructor() 方法(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在 JavaScript 的面向对象编程中,JavaScript 类(class) constructor() 方法是构建对象的核心机制。它如同程序设计中的“蓝图”,决定了对象的初始状态与行为。对于编程初学者而言,理解 classconstructor() 的关系,是掌握对象导向编程的第一步;而中级开发者则可以通过深入探索其高级特性,提升代码的结构化与可维护性。本文将从基础概念出发,结合实际案例,逐步解析这一主题的要点与应用场景。


什么是 JavaScript 类(Class)和 constructor() 方法?

类:对象的模板工厂

JavaScript 的 class 是一种语法糖,用于定义对象的模板。类本身并不直接创建对象,而是通过 new 关键字实例化对象。可以将类想象为一座“工厂”,它的职责是按照预设的规则生产标准化的产品(即对象)。例如:

class Car {
  // 类的属性和方法定义
}

const myCar = new Car(); // 通过 new 关键字实例化对象

constructor() 方法:对象的初始化引擎

每个类都包含一个默认的 constructor() 方法。当使用 new 实例化对象时,此方法会自动执行,负责初始化对象的属性与状态。如果未显式定义 constructor(),JavaScript 会默认生成一个空方法。例如:

class Car {
  constructor() {
    this.type = "Sedan";
    this.color = "Red";
  }
}

const myCar = new Car(); // myCar 对象已初始化 type 和 color 属性

比喻说明
constructor() 就像工厂的“流水线起点”。当一辆新车(对象)进入生产线时,流水线(constructor())会为它安装引擎、轮胎等基础部件(属性初始化)。没有这个起点,工厂就无法生产出完整的车辆。


如何定义和使用 constructor() 方法?

基础语法与参数传递

通过 constructor() 方法,可以接收参数并赋值给实例的属性。例如:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const alice = new Person("Alice", 30);
console.log(alice.name); // 输出 "Alice"

关键点

  1. constructor() 的参数是可选的,但通常用于传递初始值。
  2. 通过 this 关键字将参数绑定到实例属性。
  3. 如果未定义 constructor(),则默认生成一个无参数的方法。

默认参数与解构赋值

在 ES6+ 中,可以使用默认参数和解构赋值简化参数传递:

class Student {
  constructor({ name = "Guest", age = 18 } = {}) {
    this.name = name;
    this.age = age;
  }
}

const guestStudent = new Student(); // name: "Guest", age: 18
const john = new Student({ name: "John", age: 20 }); // name: "John", age: 20

优势

  • 默认参数避免了属性未定义的潜在错误。
  • 解构赋值使代码更简洁,尤其适用于复杂对象的初始化。

constructor() 在继承中的角色

继承与 super() 的必要性

在继承场景中,子类必须通过 super() 显式调用父类的 constructor()。这是因为子类的构造函数默认会先执行父类的构造函数,以确保对象的原型链正确建立。例如:

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 必须先调用 super()
    this.breed = breed;
  }
}

const myDog = new Dog("Buddy", "Golden Retriever");
console.log(myDog.name); // 输出 "Buddy"

比喻说明
super() 是连接父类与子类的“桥梁”。如果子类不调用 super(),则父类的构造函数无法执行,导致原型链断裂,对象可能缺少关键属性或方法。

父类未定义 constructor() 的情况

如果父类未显式定义 constructor(),则子类仍需调用 super(),因为父类默认有一个空的 constructor()。例如:

class ParentClass {} // 父类无显式 constructor()

class ChildClass extends ParentClass {
  constructor() {
    super(); // 仍需调用
    this.childProperty = "Hello";
  }
}

constructor() 的高级用法

静态方法与实例方法的区别

类中可以通过 static 关键字定义静态方法,这些方法不属于实例,而是直接挂载在类本身上。而 constructor() 是实例方法,仅在实例化时执行。例如:

class MathUtil {
  constructor() {
    // 实例方法,仅在 new MathUtil() 时触发
    this.value = 0;
  }

  static add(a, b) {
    // 静态方法,无需实例化即可调用
    return a + b;
  }
}

console.log(MathUtil.add(1, 2)); // 输出 3(无需 new)

使用 getter 和 setter 初始化属性

通过 constructor() 可以结合 getset 访问器,实现属性的动态初始化或验证。例如:

class User {
  constructor(email) {
    this.email = email; // 触发 setter
  }

  set email(value) {
    if (!value.includes("@")) {
      throw new Error("Invalid email format");
    }
    this._email = value;
  }

  get email() {
    return this._email;
  }
}

// 成功初始化
const user1 = new User("test@example.com");

// 抛出错误
const user2 = new User("invalid-email"); // Error: Invalid email format

常见问题与解决方案

问题 1:忘记调用 super() 导致错误

在子类的 constructor() 中未调用 super(),会抛出错误:

class Parent {}
class Child extends Parent {
  constructor() {
    // 错误:必须调用 super()
    this.property = "test";
  }
}

new Child(); // Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from constructor

解决方案:在子类构造函数中,任何操作 this 或返回前,必须先调用 super()

问题 2:参数传递不匹配

如果父类的 constructor() 需要参数,但子类未提供足够的参数,会导致错误。例如:

class Animal {
  constructor(name) {} // 需要 name 参数
}

class Dog extends Animal {
  constructor() {
    super(); // 错误:未传递 name 参数
  }
}

解决方案:确保 super() 的参数与父类构造函数的要求一致。

其他注意事项

场景描述
默认 constructor()如果未定义 constructor(),JavaScript 会自动添加一个空方法。
私有字段与 constructor()可以通过 # 符号定义私有字段,并在构造函数中初始化。
类的继承层级多级继承时,所有子类的构造函数都需正确调用 super()

实战案例:构建一个购物车类

class ShoppingCart {
  constructor(items = []) {
    this.items = items;
    this.totalPrice = 0;
    this._calculateTotal(); // 私有方法计算总价
  }

  addItem(item, price) {
    this.items.push({ item, price });
    this._calculateTotal();
  }

  _calculateTotal() {
    this.totalPrice = this.items.reduce(
      (sum, { price }) => sum + price,
      0
    );
  }

  get total() {
    return `Total: $${this.totalPrice.toFixed(2)}`;
  }
}

const cart = new ShoppingCart([{ item: "Book", price: 15 }]);
cart.addItem("Pen", 2);
console.log(cart.total); // 输出 "Total: $17.00"

案例解析

  • constructor() 初始化购物车的 itemstotalPrice
  • _calculateTotal() 是私有方法,通过下划线命名约定表示内部使用。
  • get total 提供格式化的总价输出。

结论

JavaScript 类(class) constructor() 方法是对象初始化的核心工具,其作用贯穿从基础对象创建到复杂继承场景的整个过程。通过本文的讲解,读者应能掌握以下要点:

  1. 理解类与 constructor() 的基本语法和作用。
  2. 学会通过参数传递、默认值、解构赋值等技巧优化初始化逻辑。
  3. 掌握继承场景中 super() 的必要性及常见错误的解决方案。
  4. 结合实战案例,将理论转化为可复用的代码模式。

随着 JavaScript 生态的持续发展,类和构造函数的使用场景将更加广泛。建议读者在实际项目中多加练习,并关注 ESNext 的新特性,以进一步提升代码设计能力。

最新发布