- 原文地址:ECMAScript Classes - Keeping Things Private
- 原文做者:Milos Protic
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:ZavierTang
- 校對者:Xuyuey, lgh757079506
像往常同樣,咱們將從一些理論知識開始介紹。ES 的類是 JavaScript 中新的語法糖。它提供了一種簡潔的編寫方法,而且實現了與咱們使用原型鏈相同的功能。惟一的區別是,它看起來更像是面向對象編程了,並且,若是你是 C# 或 Java 開發者,感受會更友好。有人可能會說它們不適合 JavaScript,但對我來講,使用類或 ES5 的原型都沒有問題。javascript
它提供了一種更簡單的方式來封裝和定義多個屬性,這些屬性能夠在具體的實例對象上被訪問到。事實上,咱們能夠經過類的方式編寫更少的代碼來實現更多的功能。有了類,JavaScript 正朝着面向對象的方式發展,經過使用類,咱們能夠實現面向對象編程,而不是函數式編程。不要誤解個人意思,函數式編程並非一件壞事,實際上,這是一件好事,它也有一些優於類的好處,但這應該是另外一篇文章要討論的主題。前端
舉一個實際的例子,每當咱們想在應用程序中定義來自真實世界的事物時,咱們都會使用一個類來描述它。例如,building、car、motorcycle……它們表明一類真實的事物。java
在後端語言中,咱們有訪問修飾符或可見性級別,如 public
、private
、protected
、internal
、package
……不幸的是,JavaScript 僅以本身的方式支持前兩種方法。它不經過編寫訪問修飾符(public
或 private
)來聲明字段,JavaScript 在某種程度上假定全部的區域都是公共的,這就是我寫這篇文章的緣由。android
注意,咱們有一種方法能夠在類上聲明私有和公共的字段,可是這些字段聲明方法仍是實驗性的特性,所以還不能安全的使用它。ios
class SimCard {
number; // public field
type; // public field
#pinCode; // private field
}
複製代碼
若是沒有像 Babel 這樣的編譯器,就不支持使用上面這樣的字段聲明方式。git
封裝是編程中的一個術語,好比它用來描述某個變量是受保護的或對外部是不可見的。爲了保持數據私有而且只對內部可見,咱們須要封裝它。在本文中,咱們將使用幾種不一樣的方法來封裝私有數據。讓咱們開始吧。github
這種方式只是假定數據或變量的 private
狀態。實際上,它們是公開的,外部能夠訪問。我瞭解到的兩種最多見的定義私有狀態的習慣約定是 $
和 _
前綴。若是某個變量以這些符號做爲前綴(一般在整個應用中會規定使用某一個),那麼該變量應該做爲非公共屬性來處理。編程
class SimCard {
constructor(number, type, pinCode) {
this.number = number;
this.type = type;
// 這個屬性被定義爲私有的
this._pinCode = pinCode;
}
}
const card = new SimCard("444-555-666", "Micro SIM", 1515);
// 這裏,咱們將訪問私有的 _pinCode 屬性,這並非咱們預期的行爲
console.log(card._pinCode); // 輸出 1515
複製代碼
閉包對於控制變量的可訪問性很是有用。它被 JavaScript 開發者使用了幾十年。這種方法爲咱們提供了真正的私有性,數據對外部來講是沒法訪問的,它只能被內部訪問。這裏咱們要作的是在類構造函數中建立局部變量,並用閉包捕獲它們。要實現這個效果,方法必須定義在實例上,而不是在原型鏈上。後端
class SimCard {
constructor(number, type, pinCode) {
this.number = number;
this.type = type;
let _pinCode = pinCode;
// 這個屬性被定義爲私有的
this.getPinCode = () => {
return _pinCode;
};
}
}
const card = new SimCard("444-555-666", "Nano SIM", 1515);
console.log(card._pinCode); // 輸出 undefined
console.log(card.getPinCode()); // 輸出 1515
複製代碼
Symbol 是 JavaScript 中一種新的基本數據類型。它是在 ECMAScript 6 中引入的。Symbol()
返回的每一個值都是惟一的,這種類型的主要目的是用做對象屬性的標識符。安全
因爲咱們的意圖是在類定義的外部建立 Symbol 變量,但也不是全局的,因此引入了模塊。這樣,咱們可以在模塊內部建立私有字段,將它們定義到類的構造函數中,並經過 getter
返回 Symbol 變量對應的值。注意,咱們可使用在原型鏈上建立的方法來代替 getter
。我選擇了 getter
方法,由於這樣咱們就不須要調用函數來獲取值了。
const SimCard = (() => {
const _pinCode = Symbol('PinCode');
class SimCard {
constructor(number, type, pinCode) {
this.number = number;
this.type = type;
this[_pinCode] = pinCode;
}
get pinCode() {
return this[_pinCode];
}
}
return SimCard;
})();
const card = new SimCard("444-555-666", "Nano SIM", 1515);
console.log(card._pinCode); // 輸出 undefined
console.log(card.pinCode); // 輸出 1515
複製代碼
這裏須要指出的一點是 Object.getOwnPropertySymbols
方法,此方法可用於訪問咱們用來保存私有屬性的 Symbol 變量。上面的類中的 _pinCode
值就能夠這樣被獲取到:
const card = new SimCard("444-555-666", "Nano SIM", 1515);
console.log(card[Object.getOwnPropertySymbols(card)[0]]); // 輸出 1515
複製代碼
ECMAScript 6 還引入了 Map
和 WeakMap
。它們以鍵值對的形式存儲數據,這使得它們很是適合存儲咱們的私有變量。在咱們的示例中,Map
被定義在模塊的內部,而且在類的構造函數中設置每一個私有屬性的鍵值。這個值被類的 getter
引用,一樣,咱們不須要調用函數來獲取值。另外,請注意,考慮到 Map
自己的結構,咱們不須要爲每一個私有屬性定義 Map
映射。
const SimCard = (() => {
const _privates = new Map();
class SimCard {
constructor(number, type, pinCode, pukCode) {
this.number = number;
this.type = type;
_privates.set('pinCode', pinCode);
_privates.set('pukCode', pukCode);
}
get pinCode() {
return _privates.get('pinCode');
}
get pukCode() {
return _privates.get('pukCode');
}
}
return SimCard;
})();
const card = new SimCard("444-555-666", "Nano SIM", 1515, 45874589);
console.log(card.pinCode); // 輸出 1515
console.log(card.pukCode); // 輸出 45874589
console.log(card._privates); // 輸出 undefined
複製代碼
注意,在這種方法中,咱們也可使用普通對象而不是 Map
,並在構造函數中動態地爲其分配值。
但願這些示例對你會有幫助,而且可以用到你的工做中。若是是的話,而且你也喜歡這篇文章,那歡迎分享。這裏我只實現了 Twitter 的分享按鈕,(哈哈)但我也正在實現其餘的方式。
若是要進一步閱讀,我推薦一篇文章:JavaScript Clean Code - Best Practices
感謝你的閱讀,下一篇文章再見。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。