這幾天 JavaScript 的私有屬性又成為了前端社群熱議的話題。原因很簡單,這傢伙長這樣:
驚不驚喜!意不意外!
而且 TC39 委員會以及對此達成了一致意見,並且該提案已經進入了 stage 3。在 es 規範階段 stage 3 是候選提案,又很大的可能會進入到下一個標準。到目前為止,已經可以確定會進入 es2019(es10) 標準的有 optional-catch-binding 和 proposal-json-superset。而這個 private 大概也只能進入到 es2020 後的標準了。
TC39 委員會解釋道,他們也是做了深思熟慮最終選擇了 #
符號,而沒有使用 private
關鍵字。其中還討論了把 private
和 #
符號一起使用的方案。並且還打算預留了一個 @
關鍵字作為 protected
屬性?,不過這個 @
已經被 decorator 使用了。
我們在此就不討論這個語法到底醜不醜了,這不顯而易見的嗎,還用討論嗎。我們討論一下為什麼要使用 # 符號。
有人說,“如果這個進入了規範,我就再也不用 JavaScript了,以後專案都用 TypeScript 寫”。
這就有點由不得你了,我們看 TypeScript 的官網介紹:
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
TypeScript 是 JavaScript 的超集,言外之意就是 js 有的 ts 也全都有。對於一個已經進入 stage 3 的提案,ts 當然不會坐視不理的。ts 也正在抓緊支援這個語法:All Bloomberg Changes for Private Fields and Methods。
也有人疑問:“為什麼 ts 的私有屬性就可以使用 private
關鍵字,而 js 就不行呢?”
因為 ts 的 private
和這個 #
語法完全是不同的東西。TypeScript 是一種靜態型別語言而不是強型別語言(有爭議)。private 只是在編譯時做檢查,最終生成的 js 程式碼中被 private
修飾的屬性依然是公開的。
那為什麼不使用下劃線(_
)呢,畢竟這個已經是私有屬性的非正式的最佳實踐了?
之所以不選擇下劃線是出於相容性考慮,另一個由於相容性而妥協的提案是 JavaScript 社群由一個庫引發的“smoosh門”事件到底怎麼回事? 由於下劃線是合法的 js 變數識別符號所以很多的程式碼都使用了下劃線,如果新的 js 規範把下劃線開頭的變數作為私有屬性,那麼會導致很多就程式碼無法執行。比如知名的 lodash 和 underscore 庫就是使用的下劃線,這兩個庫每週在 npm 的下載量是 2 千萬。
“為什麼不使用 private?”
這裡有一個經典的例子:
class Foo {
private value;
equals(foo) {
return this.value === foo.value;
}
}複製程式碼
在很多物件導向語言中都有這種寫法,判斷類的兩個例項是否相等。如果在 js 中也這麼寫,會有一些問題。
在靜態型別語言中,我們可以明確的知道傳入的引數是 Foo
型別,因此在 Foo
的 equals
方法中我們可以訪問 foo
的私有屬性 value
。但是 js 是動態型別的,我們無法知道引數 foo
的型別,如果我們傳入了 {value: '123'
會,則函式的行為不符合我們的預期。這也導致了該函式有時訪問的是私有屬性,有時訪問的是公有屬性。
}
另一方面,私有屬性只在類的內部可以訪問,外部無法訪問,甚至不知道此變數的存在。因此在 JavaScript 中同名的私有屬性和公有屬性可以同時存在,兩者使用 #
做區分。
如果使用 private 進行修飾,則我們可以探測類的私有屬性:
foo.bar = 1;
// Error: `bar` is private! (boom... detected)複製程式碼
或者:
foo.bar = 1;
foo.bar;
// `undefined` (boom... detected again)複製程式碼
PS:雖然社群的整體意見是使用 private
代替 #
,但是 TC39 的意見很堅決。
最後,這種引入私有屬性的方式,其實隱式的為 js 引入了靜態型別。