JavaScript类的基本定义与实例化方法

洛溪
洛溪
发布时间:2026-10-21 | 3人浏览过
原创

在 JavaScript 开发中,有一条常见的建议是:当需要创建多个具有相同结构和行为的对象时,优先使用 class 关键字而不是工厂函数或对象字面量。这条建议的核心在于利用类的语法结构来明确“模板”与“实例”的界限,使代码更具可读性和维护性。然而,这并非铁律,理解其背后的机制以及何时不适用同样重要。

1. 类是对象的蓝图

类本身不是对象,而是创建对象的模板。定义一个类就像绘制一张建筑图纸,而实例化则是根据图纸建造具体的房屋。这种分离让开发者可以专注于定义一次逻辑,然后重复生成数据独立的实体。

使用 class 关键字定义类时,必须包含一个名为 constructor 的特殊方法。这个方法在每次使用 new 操作符创建实例时自动执行,用于初始化实例的状态。

class User {
  constructor(name, role) {
    this.name = name;
    this.role = role;
  }
}

在这段代码中,constructor 接收参数并将其赋值给 this。这里的 this 指向即将被创建的新实例。如果不定义 constructor,JavaScript 引擎会默认提供一个空的构造函数。

JavaScript类的基本定义与实例化方法

类作为蓝图生成多个独立实例的过程

2. new 操作符的执行机制

实例化的过程由 new 操作符触发。它不仅仅是一个关键字,背后涉及四个关键步骤:创建一个新对象、将新对象的原型链接到类的原型、执行构造函数并将 this 绑定到新对象、返回新对象(除非构造函数显式返回其他对象)。

const admin = new User('Alice', 'admin');
console.log(admin.name); // 输出: Alice
console.log(admin instanceof User); // 输出: true

如果忘记使用 new 直接调用类,JavaScript 会抛出 TypeError。这与普通函数不同,普通函数忘记 new 可能只会导致 this 指向全局对象或 undefined(严格模式下),而类强制要求使用 new,从而避免了隐式错误。

3. 实例方法与原型链

在类中定义的方法默认挂载在类的 prototype 上,而不是每个实例上。这意味着所有实例共享同一份方法代码,节省了内存空间。

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

  greet() {
    return `Hello, I am ${this.name}`;
  }
}

const user1 = new User('Bob');
const user2 = new User('Charlie');

console.log(user1.greet === user2.greet); // 输出: true

上述代码中,user1.greet 和 user2.greet 引用的是同一个函数。这种共享机制是原型继承的核心优势。如果在 constructor 内部定义方法,则每个实例都会拥有独立的方法副本,通常不推荐这样做,除非需要闭包捕获特定状态。

JavaScript类的基本定义与实例化方法

实例通过原型链共享同一份方法代码

4. 静态方法与工具函数

有些行为不属于某个具体实例,而是属于类本身。例如,创建一个默认用户或验证输入格式。这时可以使用 static 关键字定义静态方法。

class User {
  static createGuest() {
    return new User('Guest', 'guest');
  }
}

const guest = User.createGuest();

静态方法不能通过实例调用,只能通过类名调用。它们常用于工厂模式或工具函数,将相关逻辑封装在类的命名空间下,避免污染全局作用域。

5. getter 与 setter 的控制

直接暴露属性可能导致数据不一致。通过 get 和 set 关键字,可以在访问或修改属性时插入逻辑,实现数据验证或计算属性。

class Product {
  constructor(price) {
    this._price = price;
  }

  get price() {
    return this._price;
  }

  set price(value) {
    if (value < 0) {
      throw new Error('Price cannot be negative');
    }
    this._price = value;
  }
}

const item = new Product(100);
item.price = -10; // 抛出错误

这种机制让外部调用者像访问普通属性一样使用 item.price,但内部却执行了验证逻辑,保持了接口的简洁性与数据的安全性。

JavaScript类的基本定义与实例化方法

通过getter和setter拦截属性访问与修改

6. 例外:类不是函数提升

虽然 class 在底层仍然是函数,但它不具备函数声明的提升特性。函数声明可以在定义之前调用,而类必须先定义后使用。这是因为类存在“暂时性死区”(Temporal Dead Zone)。

// 正确
class Circle {}
const c = new Circle();

// 错误
const s = new Square(); // ReferenceError
class Square {}

这一例外要求开发者注意代码的组织顺序,或者使用模块系统来管理依赖。此外,类内部默认启用严格模式,这意味着不能在类中使用未声明的变量,这也与普通非严格模式下的函数行为不同。

7. 例外:私有字段的支持差异

传统 JavaScript 没有真正的私有属性,开发者常使用下划线前缀 _property 作为约定。ES2022 引入了以 # 开头的私有字段语法,提供了语言级别的隐私保护。

class BankAccount {
  #balance = 0;

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
}

然而,这一特性在较旧的浏览器或环境中可能需要转译。如果目标环境不支持,仍需依赖闭包或 WeakMap 来模拟私有性。这是选择类语法时需要考虑的兼容性例外。

JavaScript类的基本定义与实例化方法

使用#符号定义真正的私有字段

8. 何时不使用类

尽管类很有用,但并非所有场景都适合。如果只需要一个简单的数据容器,对象字面量更轻量;如果需要灵活的闭包状态,工厂函数可能更合适。类适合那些具有明确身份、生命周期和共享行为的实体。

判断原则是:如果多个对象共享行为且需要明确的类型标识,使用类;如果只是数据的临时聚合,使用对象;如果需要复杂的闭包隔离,使用工厂函数。理解这些边界,才能在实际项目中做出恰当的选择。

小结而言,JavaScript 的类提供了结构化的对象创建方式,通过 constructor 初始化状态,通过 new 创建实例,通过原型共享方法。但需注意其无提升特性及环境兼容性问题,根据实际需求灵活选用。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表