ES6 中有類語(yǔ)法,定義類變得簡(jiǎn)單了
class Person {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
}
然而,并沒(méi)有提供私有屬性。比如上面的 Person
其實(shí)是希望在構(gòu)造的時(shí)候傳入 name
,之后不允許修改了。不過(guò),由于沒(méi)有私有屬性,所以難免有人會(huì)這樣干:
Person james = new Person("James");
james._name = "Tom"; // God Save Me
不過(guò),如果想定義私有成員,也有變通的方式,比如廣為留傳的 Symbol 大法
var Person = (function() {
let _name = Symbol();
class Person {
constructor(name) {
this[_name] = name;
}
get name() {
return this[_name];
}
}
return Person;
})();
其實(shí)質(zhì)在于匿名函數(shù)中的 Symbol 實(shí)例 _name
是局部變量,在外部不可訪問(wèn)。而 Symbol 由于自身的唯一性特點(diǎn),也沒(méi)法再造一個(gè)相同的出來(lái),所以就模擬出來(lái)一個(gè)私有成員了。
按照此思路,在 ES5 中其實(shí)也很容易模擬私有成員。局部變量是很容易做到的,在函數(shù)范圍內(nèi) let
和 var
是一樣的效果。問(wèn)題在于模擬 Symbol 的唯一性。
ES5 沒(méi)有 Sybmol,屬性名稱只可能是一個(gè)字符串,如果我們能做到這個(gè)字符串不可預(yù)料,那么就基本達(dá)到目標(biāo)。要達(dá)到不可預(yù)期,一個(gè)隨機(jī)數(shù)基本上就解決了。
var Person = (function() {
var _name = "00" + Math.random();
function Person(name) {
this[_name] = name;
}
Object.defineProperty(Person.prototype, "name", {
get: function() {
return this[_name];
}
});
return Person;
})();
如果這個(gè)程序在 Web 頁(yè)面中加載,那么每次刷新頁(yè)面 _name
的值都會(huì)不同,但并不會(huì)影響程序的邏輯,外部程序不會(huì)出現(xiàn)任何不適。
然而與 Symbol 方案相比,它的問(wèn)題在于這個(gè) _name
的值不會(huì)像 Symbol
一樣會(huì)隱藏起來(lái),在控制臺(tái)可以用很多種辦法把它找出來(lái)——當(dāng)然在調(diào)試階段這樣做也沒(méi)什么不可以。在開(kāi)發(fā)階段這個(gè)值仍然是不可預(yù)料的。
對(duì)于單個(gè)私有屬性的情況,有人會(huì)找到私有 Key 的規(guī)律,比如上面的私有 Key 就是以 "000."
開(kāi)始的,遍歷對(duì)象屬性很容易找出來(lái)。在多個(gè)私有 Key 的情況下,也可以通過(guò)一些技術(shù)手段來(lái)找,比如
function getPersonNameKey() {
var v = "" + Math.random();
var p = new Person(v);
for (var k in p) {
if (p[k] === v) {
return k;
}
}
}
但這些都是后話,做起來(lái)太費(fèi)勁,一般人不會(huì)這么干。何況 Symbol 也是可以遍歷的(通過(guò) Object.getOwnPropertySymbols()
),完全可以以同樣的方法來(lái)獲取私有 Key。
綜上,ES5 中模擬 Symbol 來(lái)實(shí)現(xiàn)私有屬性的目的已經(jīng)達(dá)到了。
更多建議: