怎么使用ES6 Class语法为JavaScript对象定义方法

作者:小括号

时间:2026-11-14

浏览:637

详细讲解如何使用ES6 Class语法为JavaScript对象定义原型方法、构造方法、存取器以及静态方法,剖析底层原型链机制与this绑定细节,帮助开发者写出优雅高效的面向对象JavaScript代码。

文章目录
在 ES6(ECMAScript 2015)之前,为 JavaScript 对象定义共享方法通常需要手动挂载到构造函数的 prototype 原型对象上。ES6 引入了声明式的 class 语法,通过更清晰直观的形式为对象定义原型方法、构造函数初始化、存取器(Getter/Setter)以及静态工具方法。本文将逐步讲解在 ES6 class 内部定义各类方法的具体写法、底层运作原理、this 指向机制及高频避坑要点。
怎么使用ES6 Class语法为JavaScript对象定义方法
图 1:ES6 Class 语法结构与生成的实例对象关系全景示意图

一、基础做法:定义原型方法(Prototype Methods)

在 ES6 中,定义对象方法最直接的方式是在 class 块内直接书写方法名称和参数列表。与字面量对象不同,类体内部的方法声明既不需要 function 关键字,方法之间也严禁使用逗号分隔。

当通过 new 运算符实例化该类时,类体中定义的普通方法并不会逐一拷贝到每个实例自身,而是统一挂载到类的原型对象(ClassName.prototype)上。当实例对象调用该方法时,JavaScript 引擎会沿着原型链向上查找并执行。

// 定义 User 类及其原型方法
class User {
  // 构造函数:负责初始化每个实例的专属数据
  constructor(name, role) {
    this.name = name;
    this.role = role;
  }

  // 原型方法 1:无逗号,免写 function
  sayHello() {
    return `你好,我是 ${this.name}`;
  }

  // 原型方法 2:支持传递实参
  hasPermission(requiredRole) {
    return this.role === requiredRole;
  }
}

// 实例化并调用方法
const user1 = new User('张三', 'admin');
const user2 = new User('李四', 'editor');

console.log(user1.sayHello()); // 输出: "你好,我是 张三"
console.log(user1.hasPermission('admin')); // 输出: true
console.log(user1.sayHello === user2.sayHello); // 输出: true(证明共享同一原型方法)

上述代码中,user1.sayHello === user2.sayHello 返回 true,这说明无论创建多少个实例对象,sayHello 方法只在内存中存在一份,有效避免了函数实例的重复创建与内存消耗。

怎么使用ES6 Class语法为JavaScript对象定义方法
图 2:使用 ES6 Class 语法定义与调用对象方法的4个关键执行步骤

二、深入原理:原型方法与类字段方法的机制差异

在现代前端开发中,有时会看到在类中使用等号与箭头函数定义方法的写法(属于 ES2022 类公有字段特性,通常写作 method = () => {})。许多初学者容易将两者混淆,但它们在内存开销和 this 指向机制上有着本质不同:

对比维度 普通原型方法 show() {} 类字段箭头函数 show = () => {}
存储位置 挂载在 ClassName.prototype 原型对象上 挂载在各个具体的实例对象自身上
内存占用 无论实例化多少个对象,内存中仅驻留一份函数实体 每 new 一次就创建一个独立的闭包函数实体
this 绑定 动态绑定,取决于运行时的调用者对象 词法绑定,在构造期永久固定为当前实例
回调传递表现 作为回调传递(如 setTimeout 或事件监听)时易脱离实例造成 this 丢失 作为回调传递时依然稳固指向该实例,无需额外 .bind(this)
怎么使用ES6 Class语法为JavaScript对象定义方法
图 3:原型方法与类字段箭头函数的堆内存拓扑与 this 绑定机制对比
选型建议与实践准则
在绝大多数常规面向对象开发中,应优先采用标准的原型方法声明,以确保原型链的高效共享;只有当该方法必须作为事件监听回调或定时器参数传递且需要固化 this 时,才考虑采用类字段箭头方法或在 constructor 中调用 this.method = this.method.bind(this)。

三、高级形态:存取器方法与静态方法

除了普通的原型方法,ES6 class 还支持存取器属性(Getter/Setter)以及直接附属于类对象的静态方法。

1. 存取器方法(Getter 与 Setter)

通过在方法名前添加 get 或 set 关键字,可以定义看似普通属性访问、实则触发方法调用的存取器。这种设计常用于执行数据校验、格式化派生数据或封装内部隐藏属性。

class Account {
  constructor(initialBalance) {
    // 使用下划线前缀表示内部存储变量
    this._balance = initialBalance;
  }

  // Getter:读属性时触发,不接收参数,必须有返回值
  get balanceFormatted() {
    return `¥${this._balance.toFixed(2)}`;
  }

  // Setter:赋值时触发,接收单个参数进行校验
  set balance(value) {
    if (typeof value !== 'number' || value < 0) {
      throw new Error('金额必须是大于等于 0 的合法数值');
    }
    this._balance = value;
  }
}

const acc = new Account(100);
console.log(acc.balanceFormatted); // 输出: "¥100.00"(像读属性一样,无括号)

acc.balance = 250.5; // 触发 setter 赋值校验
console.log(acc.balanceFormatted); // 输出: "¥250.50"

2. 静态方法(Static Methods)

如果一个方法主要用于提供通用工具函数或实现工厂构造模式,且不需要依赖特定实例的内部状态,可以使用 static 关键字。静态方法直接挂载在类(即构造函数自身)上,不能通过实例对象调用。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  // 实例方法:计算自身到原点的距离
  distanceFromOrigin() {
    return Math.hypot(this.x, this.y);
  }

  // 静态方法:计算两点之间的直线距离(工具方法)
  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;
    return Math.hypot(dx, dy);
  }
}

const p1 = new Point(3, 4);
const p2 = new Point(6, 8);

console.log(p1.distanceFromOrigin()); // 输出: 5(实例方法)
console.log(Point.distance(p1, p2)); // 输出: 5(静态方法由类名直接调用)
// p1.distance(p1, p2); // 报错: TypeError: p1.distance is not a function
怎么使用ES6 Class语法为JavaScript对象定义方法
图 4:ES6 类四类方法(原型方法、存取器、静态方法、重写方法)的调用归属与路由路径

四、继承体系中的方法重写与 super 委托

在继承体系中,子类通过 extends 继承父类后,可以自然复用父类的所有原型方法和静态方法。如果子类需要定制行为,可以直接在子类中声明同名方法以覆盖(重写)父类方法,并使用 super.methodName() 向上委托执行父类的原逻辑。

class Notification {
  constructor(title) {
    this.title = title;
  }

  send() {
    return `[通知] 标题: ${this.title}`;
  }
}

// EmailNotification 继承自 Notification
class EmailNotification extends Notification {
  constructor(title, email) {
    // 必须在访问 this 之前先调用 super()
    super(title);
    this.email = email;
  }

  // 重写父类 send 方法
  send() {
    // 借助 super 调用父类已有的 send 逻辑
    const baseInfo = super.send();
    return `${baseInfo} | 发送至: ${this.email}`;
  }
}

const notice = new EmailNotification('系统维护', 'service@example.com');
console.log(notice.send());
// 输出: "[通知] 标题: 系统维护 | 发送至: service@example.com"

五、核心避坑要点与总结

在实际编码中,为 ES6 类定义和调用方法时,以下四个场景最容易引发运行时错误,请务必核对:

  1. 不要在方法之间书写逗号:类体语法与对象字面量({ a() {}, b() {} })不同,类体内方法声明间若加逗号会直接引发 SyntaxError: Unexpected token ','。
  2. 警惕解构或事件传递中 this 丢失:当把实例方法提取出来作为事件监听函数(如 btn.addEventListener('click', user.sayHello))时,调用时的上下文变成了 DOM 元素而非 user 实例。如需作为独立回调使用,应在构造函数内 bind(this) 或声明为箭头函数类字段。
  3. Getter 内部不要读写同名属性:在 get title() 内如果直接 return this.title,会再次递归触发 getter,最终导致 RangeError: Maximum call stack size exceeded(调用栈溢出)。应使用如 this._title 的内部后备字段。
  4. 子类 constructor 中必须先调用 super():派生类如果显式写了构造函数,必须在执行任何涉及 this 的语句之前调用 super(),否则引擎无法完成实例环境的初始化。
怎么使用ES6 Class语法为JavaScript对象定义方法
图 5:ES6 Class 方法定义选型决策树与常见报错自查清单

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章