一、前言:为什么需要 class?
在 ES6 规范发布之前,JavaScript 实现面向对象编程主要依赖于“构造函数 + 原型链”的模式。这种写法虽然功能强大,但代码分散、逻辑不够直观,对于从 Java、C++ 等传统面向对象语言转过来的开发者而言,理解成本较高。
ES6 引入的 class 语法,从形式上弥补了这一缺憾。它提供了一种更清晰、更规范的面向对象写法,底层依然是基于原型的继承机制,但上层语法更加优雅,极大地提升了代码的可读性和可维护性。
二、class 基本语法
class 本质上是创建对象的模板,它整合了构造函数、实例方法和静态方法,让代码组织更清晰。
javascript
class Person {
// 构造函数:使用 new 关键字时自动调用
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法:自动挂载到原型对象上
sayHello() {
console.log(`大家好,我是${this.name},今年${this.age}岁`);
}
// 静态方法:属于类本身,不属于实例
static isAdult(age) {
return age >= 18;
}
}
// 创建实例
const p = new Person("张三", 20);
p.sayHello();
// 调用静态方法
console.log(Person.isAdult(20)); // true
核心特点:
-
语法简洁:无需手动操作
prototype,方法自动挂载到原型链。 -
结构清晰:构造函数、实例方法、静态方法一目了然。
-
高可读性:更符合传统面向对象的编程习惯。
三、class 继承:extends 与 super
继承是面向对象的三大特性之一。在 ES6 中,通过 extends 关键字可以轻松实现子类对父类的继承,super 则用于调用父类的构造函数或方法。
javascript
// 父类
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} 正在吃东西`);
}
}
// 子类继承父类
class Dog extends Animal {
constructor(name, breed) {
// 注意:子类构造函数中必须先调用 super(),才能使用 this
super(name);
this.breed = breed;
}
// 子类自有方法
bark() {
console.log(`${this.name} 汪汪叫`);
}
}
const dog = new Dog("旺财", "金毛");
dog.eat(); // 继承自父类
dog.bark(); // 自有方法
关键点总结:
-
子类的
constructor中必须先调用super(),否则会报错。 -
super指向父类的构造函数。 -
子类可以重写父类的方法,实现多态。
四、class 底层原理:语法糖的本质
虽然 class 的写法看起来很像传统面向对象的类,但它的底层实现依然是原型链。通过类型检查可以轻松验证这一点:
javascript
class Person {}
console.log(typeof Person); // 'function'
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true
结论:
-
class本质上是构造函数的语法糖。 -
类中的方法依然挂载在构造函数的
prototype属性上。 -
继承关系仍然依靠原型链实现,没有改变 JavaScript 的原型本质。
五、class 高级特性
1. getter / setter
通过 get 和 set 关键字,可以对属性的读取和赋值进行拦截和控制。
javascript
class User {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(val) {
if (val.length < 2) {
console.log("姓名长度不能小于2");
return;
}
this._name = val;
}
}
2. 静态属性
静态属性属于类本身,而不是类的实例,可以直接通过类名访问。
javascript
class User {
static role = "user";
}
console.log(User.role); // 'user'
3. 私有属性(ES2022 正式特性)
以 # 开头的属性为私有属性,只能在类内部访问,外部无法直接获取或修改,增强了封装性。
javascript
class User {
#password = "123456";
getPassword() {
return this.#password;
}
}
const user = new User();
console.log(user.getPassword()); // '123456'
console.log(user.#password); // SyntaxError: Private field '#password' must be declared in an enclosing class
六、class vs 构造函数(对比)
| 对比维度 | 构造函数 + 原型链 | ES6 class |
|---|---|---|
| 可读性 | 一般,代码分散 | 极高,结构清晰 |
| 可维护性 | 一般,手动维护原型较繁琐 | 高,语法封装性好 |
| 工程化 | 不适合复杂项目 | 现代框架首选,支持工程化 |
目前主流框架如 Vue3、React 都广泛采用 class 或类式写法,足以证明其工程化优势。
七、高频面试题
1. ES6 的 class 和 ES5 的构造函数有什么区别?
-
语法差异:class 语法更简洁,内置方法自动挂载原型。
-
提升(Hoisting):函数声明会提升,类声明不会,必须先声明后使用。
-
严格模式:类内部默认是严格模式。
-
枚举性:类内部方法不可枚举,构造函数原型上的方法默认可枚举。
2. 子类构造函数中为什么要先调用 super()?
因为子类的 this 对象需要先通过父类的构造函数进行构建,调用 super() 相当于执行父类的构造函数,否则子类无法获得父类中定义的属性和方法。
3. 如何实现一个不能被实例化的类?
可以通过将构造函数设为私有,或利用静态方法返回实例的方式实现。常见写法是使用 new.target 进行限制。
javascript
class CannotNew {
constructor() {
if (new.target === CannotNew) {
throw new Error("This class cannot be instantiated");
}
}
}
4. 私有属性的实现原理是什么?
私有属性通过编译时的语法限制实现,外部无法访问。在运行时,可以通过 WeakMap 或 Symbol 模拟,但 ES2022 原生支持的 # 写法更严格、更安全。
八、总结
-
class使 JavaScript 的面向对象编程更加规范、易于维护。 -
constructor负责实例初始化,实例方法自动挂载在原型上。 -
extends与super提供了简洁高效的继承实现。 -
class本质是语法糖,底层仍基于原型链机制。 -
支持 getter/setter、静态属性、私有属性等高级特性,满足复杂场景需求。
掌握 class,不仅是掌握 JavaScript 面向对象的标准写法,更是迈向现代工程化开发的关键一步。
下一篇:执行上下文、作用域链与闭包强化。
:ES6 Class 语法与继承:从原型到面向对象糖衣&spm=1001.2101.3001.5002&articleId=159470649&d=1&t=3&u=5d445b6151f74d62ab32b17934a79226)

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



