如何在ES6中管理類的私有資料

發表於2016-05-15
對建構函式來說,前兩種方法在 ES5 中已經很常見了,後兩種方法是 ES6 中新出現的。現在我們在同一個案例上分別用這四種方法來實踐一下:

1. 在類的建構函式作用域中處理私有資料成員

我們要演示的這段程式碼是一個名為 Countdown 的類在 counter(初始值為 counter)變成0時觸發一個名為 action 的回撥函式。其中 actioncounter 兩個引數應被儲存為私有資料。

在這個實現方案中,我們將 actioncounter 儲存在 constructor 這個類的環境裡面。環境是指JS引擎儲存引數和本地變數的內部資料結構,變數存在即可,無論是否進入一個新的作用域(例如通過一個函式呼叫或者一個類呼叫)。來看看程式碼:

然後這樣使用 Countdown:

優點:

  • 私有資料非常安全;
  • 私有屬性的命名不會與其他父類或子類的私有屬性命名衝突。

缺點:

  • 當你需要在建構函式內把所有方法(至少那些需要用到私有資料的方法)新增到例項的時候,程式碼看起來就沒那麼優雅了;
  • 作為例項方法,程式碼會浪費記憶體;如果作為原型方法,則會被共享。

關於此方法的更多內容請參考:《Speaking Javascript》的 Private Data in the Environment of a Constructor (Crockford Privacy Pattern) (建構函式環境中的私有資料)章節。

2. 通過命名約定來標記私有屬性

下面的程式碼將私有資料儲存在新增了前置下劃線命名的屬性中:

優點:

  • 程式碼比較美觀;
  • 可以使用原型方法。

缺點:

  • 不夠安全,只能用規範去約束使用者程式碼;
  • 私有屬性的命名容易衝突。

3. 通過 WeakMaps 儲存私有資料

有一個利用 WeakMap 的小技巧,結合了方法一和方法二各自的優點:安全性和能夠使用原型方法。可以參考以下程式碼:我們利用 _counter_action 兩個WeakMap來儲存私有資料。

_counter_action 這兩個 WeakMap 都分別指向各自的私有資料。由於 WeakMap 的設計目的在於鍵名是物件的弱引用,其所對應的物件可能會被自動回收,只要不暴露 WeakMap ,私有資料就是安全的。如果想要更加保險一點,可以將 WeakMap.prototype.getWeakMap.prototype.set 儲存起來再呼叫(動態地代替方法)。這樣即使有惡意程式碼篡改了可以窺探到私有資料的方法,我們的程式碼也不會受到影響。但是,我們只保護我們的程式碼不受在其之後執行的程式碼的干擾,並不能防禦先於我們程式碼執行的程式碼。

優點:

  • 可以使用原型方法;
  • 比屬性命名約定更加安全;
  • 私有屬性命名不會衝突。

Con:

  • 程式碼不如命名約定優雅。

4. 使用Symbol作為私有屬性的鍵名

另外一個儲存私有資料的方式是用 Symbol 作為其屬性的鍵名:

每一個 Symbol 都是唯一的,這就是為什麼使用 Symbol 的屬性鍵名之間不會衝突的原因。並且,Symbol 某種程度上來說是隱式的,但也並不完全是:

優點:

  • 可以使用原型方法;
  • 私有屬性命名不會衝突。

缺點:

  • 程式碼不如命名約定優雅;
  • 不太安全:可以通過 Reflect.ownKeys() 列出一個物件所有的屬性鍵名(即使用了 Symbol)。

延伸閱讀:

相關文章