ES6 引入的 Class 语法,本质是 JavaScript 原型链继承的语法糖—— 它没有改变 JS 基于原型的面向对象本质,但提供了更简洁、更符合传统 OOP 语言(如 Java、C#)的语法形式,让代码结构更清晰、可读性更强。
本文将从核心概念、语法细节、继承机制、高级特性四个维度,全面解析 ES6 Class。
一、为什么需要 Class?—— 原型链的 “痛点”
在 ES6 之前,JS 通过构造函数 + 原型链实现面向对象,但存在明显问题:
- 语法松散:构造函数、原型方法、静态方法的定义分散,代码结构混乱;
- 语义模糊:
prototype、__proto__概念抽象,新手难以理解; - 继承复杂:实现继承需要手动绑定
prototype、修正constructor,易出错(如Child.prototype = new Parent()可能继承父类实例属性)。
ES6 Class 正是为解决这些问题而生 —— 它将原型链的底层逻辑封装成直观的语法,让开发者无需关注 prototype 细节,就能高效编写面向对象代码。
二、Class 核心语法:从定义到实例化
1. 基本定义与实例化
Class 的定义有两种形式:类声明(不可提升)和 类表达式(可匿名 / 命名)。
(1)类声明(Class Declaration)
// 类声明(不可提升,类似 let/const)
class Person {
// 构造函数:实例化时执行,用于初始化实例属性
constructor(name, age) {
this.name = name; // 实例属性(每个实例独立)
this.age = age;
}
// 原型方法(挂载到 Person.prototype,所有实例共享)
sayHi() {
console.log(`Hi, I'm ${this.name}, ${this.age} years old`);
}
// 静态方法(挂载到类本身,通过类调用,不被实例继承)
static createAdult(name) {
return new Person(name, 18); // 静态方法可创建实例
}
}
// 实例化:必须用 new 关键字(否则报错)
const alice = new Person("Alice", 20);
alice.sayHi(); // 输出:Hi, I'm Alice, 20 years old
// 调用静态方法
const bob = Person.createAdult("Bob");
bob.sayHi(); // 输出:Hi, I'm Bob, 18 years old
(2)类表达式(Class Expression)
// 匿名类表达式
const Animal = class {
constructor(type) {
this.type = type;
}
roar() {
console.log(`${this.type} is roaring`);
}
};
// 命名类表达式(仅在类内部可访问类名)
const Dog = class DogClass {
constructor(name) {
this.name = name;
}
getClassName() {
return DogClass.name; // 内部可访问 DogClass
}
};
const dog = new Dog("Wangwang");
console.log(dog.getClassName()); // 输出:DogClass
console.log(DogClass); // 报错:DogClass is not defined(外部不可访问)
2. 关键细节:Class 与原型链的对应关系
Class 语法本质是原型链的封装,二者的对应关系如下(以上述 Person 类为例):
| Class 语法 | 原型链底层逻辑 |
|---|---|
constructor 方法 | 对应构造函数 Person |
sayHi() 原型方法 | 等价于 Person.prototype.sayHi = function() {} |
static createAdult() | 等价于 Person.createAdult = function() {} |
new Person() 实例化 | 本质是 new 构造函数,实例的 __proto__ 指向 Person.prototype |
验证:
const alice = new Person("Alice", 20);
console.log(alice.__proto__ === Person.prototype); // true(实例原型指向类原型)
console.log(alice.sayHi === Person.prototype.sayHi); // true(实例方法来自原型)
console.log(Person.createAdult === Person.prototype.createAdult); // false(静态方法在类本身)
三、Class 继承:extends 与 super
ES6 用 extends 关键字实现继承,比 ES5 的 “原型链继承 + 构造函数继承” 简洁得多,且支持继承原生构造函数(如 Array、Date)。
1. 基本继承
// 父类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log(`Hi, ${this.name}`);
}
static info() {
console.log("This is Person class");
}
}
// 子类继承父类
class Student extends Person {
// 子类构造函数:必须先调用 super(),再使用 this
constructor(name, age, studentId) {
super(name, age); // 调用父类构造函数(初始化 name、age)
this.studentId = studentId; // 子类实例属性
}
// 重写父类原型方法
sayHi() {
super.sayHi(); // 调用父类原型方法
console.log(`My student ID is ${this.studentId}`);
}
// 子类静态方法
static study() {
super.info(); // 调用父类静态方法
console.log("Students are studying");
}
}
// 实例化子类
const tom = new Student("Tom", 18, "S1001");
tom.sayHi();
// 输出:
// Hi, Tom
// My student ID is S1001
// 调用子类静态方法
Student.study();
// 输出:
// This is Person class
// Students are studying
// 验证继承关系
console.log(tom instanceof Student); // true
console.log(tom instanceof Person); // true(子类实例也是父类实例)
console.log(Student.__proto__ === Person); // true(子类的原型指向父类,静态方法继承)
console.log(Student.prototype.__proto__ === Person.prototype); // true(子类原型的原型指向父类原型,实例方法继承)
2. super 的核心作用
super 是继承中的关键,有两种用法:
- 作为函数:仅能在子类
constructor中调用,代表父类构造函数,用于初始化父类属性(必须先调用super(),否则子类this未定义); - 作为对象:
- 在原型方法中:指向父类原型对象(
Person.prototype),可调用父类原型方法(如super.sayHi()); - 在静态方法中:指向父类本身(
Person),可调用父类静态方法(如super.info())。
- 在原型方法中:指向父类原型对象(
⚠️ 注意:
- 子类没有
constructor时,会默认生成一个并调用super(...arguments); - 若子类有
constructor,必须显式调用super(),且不能在super()前使用this。
3. 继承原生构造函数
ES5 无法继承原生构造函数(如 Array),因为原生构造函数的内部属性无法通过原型链继承;但 ES6 extends 可以:
// 继承 Array,创建一个带额外方法的数组类
class MyArray extends Array {
// 新增方法:获取数组中大于 n 的元素
filterGreaterThan(n) {
return this.filter(item => item > n);
}
}
const arr = new MyArray(1, 3, 5, 7);
console.log(arr.filterGreaterThan(3)); // [5,7](继承 Array 的 filter 方法)
console.log(arr instanceof MyArray); // true
console.log(arr instanceof Array); // true(仍是数组实例)
四、Class 高级特性
1. 实例属性的新写法(ES2022+)
传统写法中,实例属性需在 constructor 中通过 this.xxx 定义;ES2022 支持直接在类体顶层定义实例属性(更简洁):
class Person {
// 直接定义实例属性(无需写在 constructor 中)
name = "Unknown";
age = 0;
constructor(name) {
this.name = name; // 可覆盖默认值
}
}
const alice = new Person("Alice");
console.log(alice.name); // Alice
console.log(alice.age); // 0
2. 私有属性与方法(# 前缀)
ES2022 引入私有属性 / 方法,用 # 前缀标识,仅能在类内部访问,外部无法读取或修改(安全性更高):
class Person {
#secret; // 私有属性(必须声明)
name;
constructor(name, secret) {
this.name = name;
this.#secret = secret; // 初始化私有属性
}
#privateMethod() { // 私有方法
return this.#secret;
}
getSecret() {
return this.#privateMethod(); // 类内部可访问私有方法/属性
}
}
const alice = new Person("Alice", "I like cats");
console.log(alice.name); // Alice
console.log(alice.#secret); // 报错:Private field '#secret' must be declared in an enclosing class
console.log(alice.#privateMethod()); // 报错:私有方法不可外部调用
console.log(alice.getSecret()); // I like cats(通过公有方法间接访问)
3. getter 与 setter
用于封装属性的读取和赋值逻辑,类似 ES5 的 Object.defineProperty,但语法更简洁:
class Person {
constructor(name) {
this._name = name; // 约定 _ 前缀为“受保护”属性(非真正私有)
}
// getter:读取 name 时触发
get name() {
return this._name.trim(); // 处理:去除空格
}
// setter:修改 name 时触发
set name(newName) {
if (!newName) throw new Error("Name cannot be empty"); // 校验:非空
this._name = newName;
}
}
const alice = new Person(" Alice ");
console.log(alice.name); // Alice(getter 处理)
alice.name = " Bob ";
console.log(alice.name); // Bob(setter 校验 + getter 处理)
alice.name = ""; // 报错:Name cannot be empty(setter 校验失败)
4. 静态属性(ES2022+)
静态属性是类本身的属性(非实例属性),ES2022 支持直接在类体顶层用 static 定义:
class MathUtil {
static PI = 3.1415926; // 静态属性
static add(a, b) { // 静态方法
return a + b;
}
}
console.log(MathUtil.PI); // 3.1415926
console.log(MathUtil.add(2, 3)); // 5
5. 类的动态方法 / 属性
Class 支持通过计算属性名定义动态方法 / 属性(用 [] 包裹表达式):
const methodName = "sayHi";
const propName = "age";
class Person {
[propName] = 18; // 动态实例属性
[methodName]() { // 动态原型方法
console.log(`Hi, I'm ${this[propName]} years old`);
}
static [methodName + "Static"]() { // 动态静态方法
console.log("Static sayHi");
}
}
const alice = new Person();
console.log(alice.age); // 18
alice.sayHi(); // Hi, I'm 18 years old
Person.sayHiStatic(); // Static sayHi
五、Class 与 ES5 构造函数的区别
| 特性 | ES6 Class | ES5 构造函数 + 原型链 |
|---|---|---|
| 语法形式 | 简洁、结构化,类似传统 OOP 语言 | 松散,需手动操作 prototype |
| 提升行为 | 不可提升(类似 let/const) | 函数声明可提升 |
| 实例化要求 | 必须用 new(否则报错) | 可不用 new(this 指向全局,易出错) |
| 继承机制 | extends 直接继承,支持原生构造函数 | 需组合原型链 + 构造函数,不支持原生继承 |
| 私有成员 | 支持(# 前缀) | 无原生支持(依赖闭包模拟,不彻底) |
| 静态成员 | 原生支持 static 关键字 | 需手动给构造函数赋值(如 Person.staticMethod = ...) |
六、总结
ES6 Class 是原型链的语法糖,核心价值在于:
- 简化面向对象代码的编写,降低理解成本;
- 统一类的定义、继承、静态成员等语法;
- 支持私有属性、原生构造函数继承等高级特性(ES2022+ 增强)。
使用建议:
- 编写面向对象代码时,优先使用 Class 语法(替代 ES5 构造函数);
- 理解 Class 与原型链的对应关系,避免 “语法糖依赖”(遇到复杂场景需懂底层逻辑);
- 结合
static、私有成员、getter/setter 等特性,提升代码的安全性和可读性。
虽然 Class 封装了原型链,但 JS 本质仍是基于原型的语言 ——Class 没有引入新的面向对象模型,只是让原型链的使用更优雅。


11万+

被折叠的 条评论
为什么被折叠?



