JS面向?qū)ο笾畯膒rototype到class再到私有屬性詳解
前言
這一篇將探討 JavaScript 中的面向?qū)ο缶幊蹋∣OP)。對于從 Java、C# 轉(zhuǎn)過來的開發(fā)者來說,以前 JS 的原型繼承機制往往讓人摸不著頭腦。ES6 引入的 class 關(guān)鍵字極大地降低了心智負擔(dān),雖然它本質(zhì)上是“語法糖”,但它讓代碼結(jié)構(gòu)更加清晰、標(biāo)準(zhǔn)。
1. 引言
在 ES6(ES2015)之前,JavaScript 想要實現(xiàn)“類”和“繼承”是非常痛苦的。因為 JS 本質(zhì)是基于原型的,而不是基于類的。如果你在網(wǎng)上搜“JS 繼承五種寫法”,你會發(fā)現(xiàn)組合繼承、寄生組合繼承等讓人頭禿的代碼:
// ES5 之前的“恐怖”繼承寫法
function Parent(name) { this.name = name; }
Parent.prototype.say = function() { console.log(this.name); };
function Child(name, age) {
Parent.call(this, name); // 借用構(gòu)造函數(shù)
this.age = age;
}
Child.prototype = new Parent(); // 原型鏈繼承
Child.prototype.constructor = Child; // 修正 constructor 指向
這不怪開發(fā)者,怪語言設(shè)計太底層。
ES6 引入了 class 關(guān)鍵字,終于給了我們一個**“看起來像樣”的面向?qū)ο笳Z法。雖然它底層依然是原型繼承,但它把復(fù)雜的細節(jié)封裝了起來。到了 ES2022,甚至支持了真正的私有屬性**。
本文將帶你掌握:
- 為什么
class只是語法糖,但必須得用? - 如何優(yōu)雅地使用
extends和super實現(xiàn)繼承? - 如何使用
#定義真正的私有變量。
2. 正文
2.1 ES5 的“原型”寫法(快速回顧)
在深入 class 之前,只需記住一點:ES5 的類本質(zhì)是一個構(gòu)造函數(shù)。
function Animal(name) {
this.name = name;
}
// 方法掛載在原型上,所有實例共享
Animal.prototype.speak = function() {
console.log(this.name + " makes a noise.");
};
這種寫法把屬性(構(gòu)造函數(shù)內(nèi))和方法(原型上)分開了,對于 Java/C++ 程序員來說非常不直觀。
2.2 ES6class—— 更加優(yōu)雅的語法糖
ES6 的 class 可以看作是一個語法糖,它讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法。
1. 定義類與構(gòu)造函數(shù)
class Animal {
// 構(gòu)造方法
constructor(name) {
this.name = name;
}
// 實例方法(等同于定義在 prototype 上)
speak() {
console.log(`${this.name} makes a noise.`);
}
}
const a = new Animal("Dog");
a.speak(); // "Dog makes a noise."
注意:類的方法之間不需要逗號分隔,加了逗號會報錯!
2. 靜態(tài)方法
使用 static 關(guān)鍵字定義的方法屬于類本身,而不是實例。這非常適合工具類方法。
class Animal {
constructor(name) { this.name = name; }
// 靜態(tài)方法
static compare(a, b) {
return a.name === b.name;
}
}
const dog1 = new Animal("Dog");
const dog2 = new Animal("Dog");
// 正確:通過類調(diào)用
console.log(Animal.compare(dog1, dog2)); // true
// 錯誤:實例無法調(diào)用
// dog1.compare(); // TypeError

2.3 繼承與super的正確姿勢
ES6 使用 extends 關(guān)鍵字實現(xiàn)繼承,比 ES5 的原型鏈篡改要簡單得多。
class Dog extends Animal {
constructor(name, breed) {
// 必須先調(diào)用 super(),否則會報錯!
// super 代表父類的構(gòu)造函數(shù)
super(name);
this.breed = breed;
}
speak() {
// 調(diào)用父類的方法
super.speak();
console.log(`${this.name} barks.`);
}
}
const d = new Dog("Rex", "Bulldog");
d.speak();
// 輸出:
// "Rex makes a noise."
// "Rex barks."
核心坑點:
在子類的 constructor 中,必須在使用 this 之前調(diào)用 super()。這是因為子類沒有自己的 this 對象,而是繼承父類的 this 對象。
2.4 私有屬性與新特性
在很長一段時間里,JS 沒有真正的私有屬性(private)。大家只能靠命名約定(比如 _name)來表示“請勿訪問”,但這是防君子不防小人的。
1. ES2022:私有字段
現(xiàn)在,我們可以在屬性名前加 #(井號)來定義真正的私有屬性。帶 # 的屬性是類作用域私有的,外部無法訪問。
class BankAccount {
#balance = 0; // 私有屬性
constructor(initial) {
this.#balance = initial;
}
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const myAccount = new BankAccount(100);
myAccount.deposit(50);
console.log(myAccount.getBalance()); // 150
// console.log(myAccount.#balance); // 報錯!SyntaxError: Private field '#balance' must be declared in an enclosing class

2. new.target
用于檢測函數(shù)或構(gòu)造函數(shù)是否是通過 new 關(guān)鍵字調(diào)用的。它可以用來限制類必須被實例化,或者防止直接調(diào)用抽象類。
class Parent {
constructor() {
if (new.target === Parent) {
throw new Error("本類不能直接實例化");
}
}
}
class Child extends Parent {
constructor() {
super(); // 正常
}
}
new Child(); // ?
// new Parent(); // ? 拋出錯誤

3. 常見問題 (FAQ)
Q1:class 是一個新的數(shù)據(jù)類型嗎?
A: 不是。typeof Dog 返回的依然是 "function"。class 本質(zhì)上就是把函數(shù)包裝了一下,讓你覺得它是一個“類”。你可以把 class 看作是 function 和 prototype 的語法糖。
Q2:為什么在子類 constructor 里必須先寫 super()?
A: 這是 JS 的繼承機制決定的。子類實例的構(gòu)建是基于父類實例加工的,不先調(diào)用父類的構(gòu)造函數(shù)(生成父類 this),子類就沒有 this 可用。
Q3:什么時候該用繼承?
A: 雖然語法好看了,但設(shè)計原則依然建議 “多用組合,少用繼承”。如果你只是想復(fù)用一些工具方法,用普通對象或 Mixin 可能比繼承更靈活。只有在明確的“is-a”關(guān)系(如“狗是動物”)時才使用繼承。
4. 總結(jié)
JavaScript 的面向?qū)ο笳Z法終于進化到了主流水平:
class:提供了標(biāo)準(zhǔn)的類定義寫法,雖然底層是原型,但代碼可讀性大幅提升。extends&super:讓繼承變得簡單直觀,不再需要手動操作prototype鏈。#私有屬性:ES2022 提供了真正的訪問控制,封裝性不再是靠自覺。
最佳實踐建議:
在開發(fā) React 組件、Vue 類組件(Vue 2/3)或 Node.js 服務(wù)端類時,優(yōu)先使用
class語法。對于敏感數(shù)據(jù)(如內(nèi)部狀態(tài)、API 密鑰),務(wù)必使用#私有屬性進行保護。
到此這篇關(guān)于JS面向?qū)ο笾畯膒rototype到class再到私有屬性的文章就介紹到這了,更多相關(guān)JS prototype到class再到私有屬性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序scroll-view實現(xiàn)左右聯(lián)動
這篇文章主要為大家詳細介紹了微信小程序scroll-view實現(xiàn)左右聯(lián)動,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
Javascript 數(shù)組添加一個 indexOf 方法的實現(xiàn)代碼
Javascript 的字符串有個 indexOf 的方法,能夠返回字符在指定的字符串中的位置,非常有用,本文介紹了如何給 Javascript 數(shù)組也添加一個類似的方法。2009-09-09
jquery實現(xiàn)select下拉框美化特效代碼分享
這篇文章主要介紹了jquery實現(xiàn)select下拉框美化特效,實現(xiàn)效果簡潔大方,推薦給大家,有需要的小伙伴可以參考下。2015-08-08

