??ES6中新增了class的定義方法,可以說是對JavaScript這門語言的極大豐富荣茫。雖然其本質(zhì)上還是對象飘蚯,但是對于語言的使用將變得更加明確。在以往的開發(fā)中使用function來定義一個類兴泥,而現(xiàn)在,直接用class來聲明虾宇,將和其他語言沒有太大差別搓彻。
靜態(tài)方法
class MyClass {
constructor() {}
set(key, value) {}
get(key) {}
}
//靜態(tài)方法
而且,在ES6中新增了static關(guān)鍵字來修飾靜態(tài)方法嘱朽,例如:
class MyClass {
constructor() {}
set(key, value) {}
get(key) {}
static say(words) {
alert(words)
}
}
靜態(tài)方法將不在實例化對象的方法中旭贬,因此里面不能有this,使用的時候必須直接MyClass.say()搪泳,實例化對象不擁有這個方法稀轨。靜態(tài)方法將被共享,因此所用內(nèi)存減少岸军。
實際上奋刽,由于JavaScript的語言特性,static關(guān)鍵詞修飾的方法相當于:
class MyClass {}
MyClass.say = function() {}
而正是由于這種特性艰赞,致使靜態(tài)屬性可以被動態(tài)修改佣谐,比如:
class MyClass {
static say() {
alert("yes")
}
}
MyClass.say = function() {
alert("no")
}
但是其他語言中的private關(guān)鍵字卻沒有被ES6實現(xiàn),因此方妖,我們只能通過其他方式來實現(xiàn)它(雖然將來也是有可能實現(xiàn)的)台谍。
靜態(tài)屬性
ES6中的static只能修飾class的方法,而不能修飾屬性,所以在class中趁蕊,屬性只有兩種方式:
var sex = 0
class MyClass {
constructor({name, age}) {
this.name = name
this.age = age
}
get sex() {
return sex
}
set sex(value) {
sex = value
}
}
上面有兩種屬性的配置方式,一種是在constructor中使用.操作符仔役,另一種則是通過get, set來定義屬性掷伙。如果只有g(shù)et sex而沒有set sex,那么sex屬性是不能被修改的又兵。但是問題是任柜,這兩種方法都不是定義一個靜態(tài)屬性,靜態(tài)屬性是不需要實例化的沛厨,也就是說可以直接MyClass.sex這樣獲取宙地。目前已知可用的方式如下:
class MyClass {
static get sex() {}
static set sex() {}
}
也就是再get, set前面加static。這樣就可以直接使用MyClass.sex的方法獲取或動態(tài)設置其值逆皮,而無需實例化宅粥。
私有屬性
ES中還沒有對private的實現(xiàn)方式,也就是說在class中還無法使用這個關(guān)鍵字來修飾私有屬性或方法电谣。那么怎么實現(xiàn)私有屬性呢秽梅?首先,應該明確私有屬性的特征:
class內(nèi)部不同方法間可以使用剿牺,因此this要指向?qū)嵗瘜ο螅ū仨殻?br>
不能被外部訪問企垦,因此實例化對象$person.name既不能獲得值,也不能設定值(必須)
不能被繼承晒来,因此extends后子類不具備該屬性钞诡,但理論上可以重新手工添加(必須)
約定為前面有下劃線的this._name形式(備選)
網(wǎng)上有很多種實現(xiàn)形式,目前比較讓人認可的湃崩,主要是weakmap和symbol兩種:
const _name = new WeakMap()
class MyClass {
constructor({name, age}) {
_name.set(this, name)
}
say() {
alert(_name.get(this))
}
}
這種方法說實話荧降,并不是理想的私有屬性的方法,雖然利用weakmap可以將this作為鍵名竹习,但是形式上不好看誊抛,還是希望有this._name的解決方案。
const _counter = Symbol('counter');
const _action = Symbol('action');
class Countdown {
constructor(counter, action) {
this[_counter] = counter;
this[_action] = action;
}
dec() {
if (this[_counter] < 1) return;
this[_counter]--;
if (this[_counter] === 0) {
this[_action]();
}
}
}
使用symbol的好處就是讓形式上更漂亮整陌,雖然采用的是this[_name]而非this._name拗窃,但是這理解起來已經(jīng)非常符合我們的習慣了。但是很明顯泌辫,它有缺點随夸,即我們可以通過Reflect.ownKeys()在外部獲取這些鍵名,這并不能達到“外部不能訪問”的目的震放。還有一個缺點宾毒,就是每多一個鍵名,就要在頂上新增一個const殿遂,使用起來也不夠靈活诈铛,不過從代碼明確性的角度看乙各,這樣做也有好處,就是不會忘記自己都定義過哪些私有屬性幢竹。還有就是耳峦,這種方案無法滿足“不能被繼承”的需求,子類將繼續(xù)擁有這些屬性焕毫。雖然對于其他開發(fā)者而言蹲坷,這些屬性是不可見的,但是通過Reflect.ownkeys還是可以被發(fā)現(xiàn)邑飒。
私有方法
理論上講循签,私有屬性和私有方法的區(qū)別是,私有方法是函數(shù)疙咸。因此县匠,只需要將上面的私有屬性的存儲值替換為函數(shù)即可。但是作為方法罕扎,內(nèi)部的this必須指向其實例化對象聚唐,因此還是需要在稍作加工。
var _say = new WeakMap()
class MyClass {
constructor({name, age}) {
this.name = name
_say.set(this, () => {
alert(this.name)
})
}
}
由于使用了箭頭函數(shù)腔召,函數(shù)體內(nèi)的this和外部的this是同一個杆查,因此不會發(fā)生this指向偏移的問題。
既然私有屬性可以使用set, get來實現(xiàn)臀蛛,為何私有方法不也用它來實現(xiàn)呢亲桦?其實,完全沒有必要這樣去做浊仆,就像在class外部加一個attributions一樣客峭,我們也可以在class外部創(chuàng)建函數(shù),這些函數(shù)只有在同一個文檔中可見抡柿,因此對外部也是私有的舔琅,外部程序無法獲取。
var factories = {
a: function(options) { // 這里一定要用function洲劣,而不能用箭頭函數(shù)备蚓,因為使用箭頭函數(shù)將不能使用bind
this.render()
},
}
export default class MyClass {
render() {}
call(fun) {
if(!factories[fun]) return
return factories[fun].bind(this)
}
test() {
this.call('a')('options') // 使用call來調(diào)用私有方法,第一個括號填寫函數(shù)名囱稽,第二個括號填寫該函數(shù)的參數(shù)
}
}
如果你不想用兩個括號那么麻煩郊尝,要么不允許傳入?yún)?shù),要么可以用apply代替:
export default class MyClass {
render() {}
call(fun, ...args) {
if(!factories[fun]) return
return factories[fun].apply(this, args)
}
test() {
this.call('a', options) // 使用call來調(diào)用私有方法战惊,第一個括號填寫函數(shù)名流昏,第二個括號填寫該函數(shù)的參數(shù)
}
}
如果你還想省事,甚至可以不用把函數(shù)都包含在factories里面,直接定義函數(shù)况凉,在類里面使用apply:
function my_fun(options) {
this.render()
}
export default class MyClass {
render() {}
test() {
my_fun.apply(this, 'options')
}
}
沒人會知道你都在類內(nèi)部使用過哪些私有方法谚鄙,雖然這些方法實際上都在操作著實例化對象。
z轉(zhuǎn)載