JS中class關鍵詞的使用解析
在JavaScript中,class是ES6(2015)引入的面向對象編程語法糖,核心作用是以更簡潔、語義化的方式定義構造函數和原型方法,替代了ES5中通過function+prototype模擬類的繁瑣寫法。class本質上是對原型鏈的封裝,並未改變JS基於原型的繼承本質,但讓類的定義、繼承、方法聲明更符合傳統面嚮對象語言的直覺,是現代JS開發中實現模塊化、可複用組件的核心工具。
一、class的基礎用法
1. 核心語法結構
// 定義類(類名首字母大寫,規範)
class ClassName {
// 構造函數:實例化時執行,初始化實例屬性
constructor(參數1, 參數2) {
this.屬性1 = 參數1;
this.屬性2 = 參數2;
}
// 實例方法(掛載到原型鏈)
方法名1() {
// 邏輯代碼
}
// 靜態方法(掛載到類本身,通過類名調用)
static 方法名2() {
// 邏輯代碼
}
// 訪問器屬性(get/set)
get 屬性名() {
return this.xxx;
}
set 屬性名(值) {
this.xxx = 值;
}
}
// 實例化類(必須用new)
const 實例 = new ClassName(值1, 值2);
核心規則:
class定義的類**必須用****new**實例化,直接調用會報錯(區別於ES5構造函數);constructor是類的默認方法,實例化時自動執行,若無顯式定義,會默認生成空構造函數;- 類內部的方法默認是不可枚舉的(區別於ES5手動掛載到prototype的方法)。
2. 基礎示例:定義簡單類
// 定義Person類
class Person {
// 構造函數:初始化姓名和年齡
constructor(name, age) {
this.name = name;
this.age = age;
}
// 實例方法:打招呼
sayHello() {
console.log(`你好,我是${this.name},今年${this.age}歲`);
}
// 靜態方法:類的工具方法
static isAdult(age) {
return age >= 18;
}
// 訪問器:獲取年齡描述
get ageDesc() {
return this.age >= 18 ? "成年人" : "未成年人";
}
// 訪問器:修改年齡(做校驗)
set ageDesc(newDesc) {
console.warn("年齡描述無法直接修改,請修改age屬性");
}
}
// 實例化
const person1 = new Person("張三", 20);
// 調用實例方法
person1.sayHello(); // 輸出:你好,我是張三,今年20歲
// 訪問getter屬性(無需加括號)
console.log(person1.ageDesc); // 輸出:成年人
// 調用靜態方法(通過類名)
console.log(Person.isAdult(17)); // 輸出:false
// 錯誤用法:直接調用類(無new)
// Person("李四", 25); // 報錯:Uncaught TypeError: Class constructor Person cannot be invoked without 'new'
二、class的核心特性
1. 實例方法與靜態方法
- 實例方法:掛載到
類.prototype,只能通過實例調用,方法內this指向實例; - 靜態方法:用
static修飾,掛載到類本身,通過類名調用,方法內this指向類(而非實例)。
class MathUtil {
// 實例方法:計算平方(無意義,僅示例)
square(num) {
return num * num;
}
// 靜態方法:計算求和(工具方法,適合靜態)
static sum(a, b) {
return a + b;
}
}
const util = new MathUtil();
// 實例方法調用
console.log(util.square(5)); // 輸出:25
// 靜態方法調用(類名.方法)
console.log(MathUtil.sum(3, 4)); // 輸出:7
// 錯誤:實例無法調用靜態方法
// console.log(util.sum(1,2)); // 輸出:undefined
2. 類的繼承(extends + super)
ES6通過extends實現類的繼承,super用於調用父類的構造函數或方法,是實現繼承的核心。
// 父類:Person
class Person {
constructor(name) {
this.name = name;
}
sayName() {
console.log(`姓名:${this.name}`);
}
}
// 子類:Student(繼承Person)
class Student extends Person {
constructor(name, studentId) {
// 必須先調用super,才能使用this(子類構造函數規則)
super(name); // 調用父類構造函數,初始化name
this.studentId = studentId; // 子類自有屬性
}
// 子類實例方法
sayId() {
console.log(`學號:${this.studentId}`);
}
// 重寫父類方法
sayName() {
super.sayName(); // 調用父類的sayName
console.log(`(學生)姓名:${this.name}`);
}
}
// 實例化子類
const student = new Student("李四", "S1001");
student.sayName();
// 輸出:
// 姓名:李四
// (學生)姓名:李四
student.sayId(); // 輸出:學號:S1001
關鍵規則:
- 子類構造函數中,必須先調用**
super()**,才能使用this(否則報錯); super既可以調用父類構造函數(super(參數)),也可以調用父類方法(super.方法名());- 子類會繼承父類的實例方法、靜態方法(包括
static方法)。
3. 私有屬性/方法(ES2022)
ES2022引入#前綴定義類的私有成員,私有屬性/方法僅能在類內部訪問,外部無法讀取/修改,解決了ES5中“偽私有屬性”(如_name)可被外部修改的問題。
class User {
// 公有屬性
publicId = 1001;
// 私有屬性(#前綴)
#password = "123456";
// 實例方法:訪問私有屬性
checkPassword(input) {
return input === this.#password;
}
// 私有方法(#前綴)
#encrypt(str) {
return str + "_encrypted";
}
getEncryptedPwd() {
return this.#encrypt(this.#password);
}
}
const user = new User();
// 訪問公有屬性
console.log(user.publicId); // 輸出:1001
// 調用方法訪問私有屬性
console.log(user.checkPassword("123456")); // 輸出:true
console.log(user.getEncryptedPwd()); // 輸出:123456_encrypted
// 錯誤:外部訪問私有屬性/方法(直接報錯)
// console.log(user.#password); // 報錯:Uncaught SyntaxError: Private field '#password' must be declared in an enclosing class
// console.log(user.#encrypt("test")); // 報錯
三、class的實際應用場景
1. 封裝可複用組件(前端/Node.js)
在前端框架(React/Vue)或Node.js開發中,用class封裝組件/工具類,實現代碼複用和模塊化。
// 前端示例:封裝彈窗組件類
class Modal {
constructor(title, content) {
this.title = title;
this.content = content;
this.dom = null; // 存儲彈窗DOM元素
}
// 渲染彈窗
render() {
this.dom = document.createElement("div");
this.dom.innerHTML = `
<div class="modal-title">${this.title}</div>
<div class="modal-content">${this.content}</div>
<button class="modal-close">關閉</button>
`;
document.body.appendChild(this.dom);
// 綁定關閉事件
this.dom.querySelector(".modal-close").addEventListener("click", () => {
this.close();
});
}
// 關閉彈窗
close() {
this.dom?.remove();
}
// 靜態方法:快速創建提示彈窗
static alert(content) {
const modal = new Modal("提示", content);
modal.render();
// 3秒後自動關閉
setTimeout(() => modal.close(), 3000);
}
}
// 使用:創建自定義彈窗
const customModal = new Modal("自定義標題", "自定義內容");
customModal.render();
// 使用:快速提示彈窗
Modal.alert("操作成功!");
2. 實現單例模式
通過static方法和私有屬性,可輕鬆實現類的單例模式(確保類只有一個實例)。
class Singleton {
// 私有屬性:存儲唯一實例
static #instance = null;
constructor(name) {
this.name = name;
}
// 靜態方法:獲取唯一實例
static getInstance(name) {
if (!this.#instance) {
this.#instance = new Singleton(name);
}
return this.#instance;
}
}
// 獲取實例1
const instance1 = Singleton.getInstance("實例1");
// 獲取實例2(複用實例1)
const instance2 = Singleton.getInstance("實例2");
console.log(instance1 === instance2); // 輸出:true
console.log(instance1.name); // 輸出:實例1(僅第一次初始化生效)
3. 結合接口請求封裝服務類(前端)
在前端項目中,用class封裝API服務類,統一管理接口請求邏輯,便於維護和擴展。
class UserService {
// 基礎URL
static baseUrl = "https://api.example.com/user";
// 獲取用户信息
async getUserInfo(userId) {
try {
const res = await fetch(`${UserService.baseUrl}/${userId}`);
if (!res.ok) throw new Error(`請求失敗:${res.status}`);
return res.json();
} catch (error) {
console.error("獲取用户信息失敗:", error);
throw error;
}
}
// 更新用户信息
async updateUserInfo(userId, data) {
try {
const res = await fetch(`${UserService.baseUrl}/${userId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
if (!res.ok) throw new Error(`更新失敗:${res.status}`);
return res.json();
} catch (error) {
console.error("更新用户信息失敗:", error);
throw error;
}
}
}
// 使用服務類
const userService = new UserService();
userService.getUserInfo(1001)
.then(data => console.log("用户信息:", data))
.catch(err => console.log("處理錯誤:", err));
四、class的注意事項
1. 類的提升(hoisting)
class聲明存在“提升”但未初始化,無法在聲明前調用(區別於函數聲明),屬於“暫時性死區”。
// 錯誤:類聲明前實例化
// const p = new Person(); // 報錯:Uncaught ReferenceError: Cannot access 'Person' before initialization
class Person {}
// 正確:聲明後實例化
const p = new Person();
2. 類方法無原型(不可用new調用)
類的實例方法是普通函數,但沒有prototype屬性,無法用new調用(區別於ES5構造函數)。
class Test {
fn() {}
}
const t = new Test();
// 錯誤:實例方法無法new調用
// new t.fn(); // 報錯:Uncaught TypeError: t.fn is not a constructor
3. 繼承內置類(Array/Map等)
class可繼承JS內置類(如Array),擴展其功能,實現自定義數據結構。
// 自定義數組類:擴展求和方法
class MyArray extends Array {
sum() {
return this.reduce((total, num) => total + num, 0);
}
}
const arr = new MyArray(1, 2, 3, 4);
console.log(arr.sum()); // 輸出:10
console.log(arr instanceof Array); // 輸出:true(繼承Array)
4. 避免過度封裝
class適合封裝有狀態、有繼承關係的邏輯,若僅需簡單的工具函數集合,優先使用對象字面量或模塊導出,避免過度封裝增加複雜度。
總結
class是ES6的語法糖,本質基於原型鏈,核心用於定義類、實現繼承,讓面向對象代碼更簡潔語義化;- 核心語法包括
constructor(初始化)、實例方法、靜態方法(static)、訪問器(get/set)、繼承(extends+super); - ES2022的
#前綴可定義私有屬性/方法,解決了傳統原型模式的私有成員問題; - 適用場景:封裝可複用組件、實現單例模式、管理接口服務等,是現代JS模塊化開發的核心工具。