[譯] ECMAScript 類 —— 定義私有屬性

介紹

像往常同樣,咱們將從一些理論知識開始介紹。ES 的類是 JavaScript 中新的語法糖。它提供了一種簡潔的編寫方法,而且實現了與咱們使用原型鏈相同的功能。惟一的區別是,它看起來更像是面向對象編程了,並且,若是你是 C# 或 Java 開發者,感受會更友好。有人可能會說它們不適合 JavaScript,但對我來講,使用類或 ES5 的原型都沒有問題。javascript

它提供了一種更簡單的方式來封裝和定義多個屬性,這些屬性能夠在具體的實例對象上被訪問到。事實上,咱們能夠經過類的方式編寫更少的代碼來實現更多的功能。有了類,JavaScript 正朝着面向對象的方式發展,經過使用類,咱們能夠實現面向對象編程,而不是函數式編程。不要誤解個人意思,函數式編程並非一件壞事,實際上,這是一件好事,它也有一些優於類的好處,但這應該是另外一篇文章要討論的主題。前端

舉一個實際的例子,每當咱們想在應用程序中定義來自真實世界的事物時,咱們都會使用一個類來描述它。例如,building、car、motorcycle……它們表明一類真實的事物。java

可訪問範圍

在後端語言中,咱們有訪問修飾符或可見性級別,如 publicprivateprotectedinternalpackage……不幸的是,JavaScript 僅以本身的方式支持前兩種方法。它不經過編寫訪問修飾符(publicprivate)來聲明字段,JavaScript 在某種程度上假定全部的區域都是公共的,這就是我寫這篇文章的緣由。android

注意,咱們有一種方法能夠在類上聲明私有和公共的字段,可是這些字段聲明方法仍是實驗性的特性,所以還不能安全的使用它。ios

class SimCard {
  number; // public field
  type; // public field
  #pinCode; // private field
}
複製代碼

若是沒有像 Babel 這樣的編譯器,就不支持使用上面這樣的字段聲明方式。git

定義私有屬性 —— 封裝

封裝是編程中的一個術語,好比它用來描述某個變量是受保護的或對外部是不可見的。爲了保持數據私有而且只對內部可見,咱們須要封裝它。在本文中,咱們將使用幾種不一樣的方法來封裝私有數據。讓咱們開始吧。github

1. 習慣約定

這種方式只是假定數據或變量的 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
複製代碼

2. 閉包

閉包對於控制變量的可訪問性很是有用。它被 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
複製代碼

3. Symbols 和 Getters

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

複製代碼

4. Map 和 Getters

ECMAScript 6 還引入了 MapWeakMap。它們以鍵值對的形式存儲數據,這使得它們很是適合存儲咱們的私有變量。在咱們的示例中,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 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索