JavaScript實(shí)現(xiàn)私有屬性的幾種方式小結(jié)
什么是私有屬性
我們不摳定義,用大白話來說,如果類中的某個(gè)屬性只能在類內(nèi)部使用,在類外部(比如通過類的實(shí)例)訪問不到,這個(gè)屬性就是私有屬性。
我們用下面的代碼舉例說明,當(dāng)然了,代碼中的屬性并不是私有屬性,只是為了說明私有屬性是怎么一回事:
class Person {
name = 'name' // 我們假設(shè)name是一個(gè)私有屬性,當(dāng)然,它現(xiàn)在不是
getName() {
return this.name // 類內(nèi)部可以訪問私有屬性
}
}
const person = new Person()
person.getName() // 'name'
person.name // 嘗試直接在類外部訪問私有屬性會(huì)報(bào)錯(cuò)
如何實(shí)現(xiàn)
早期JavaScript并不支持私有屬性,所以只能通過一些變通的方法曲線救國。
基于命名規(guī)范的的弱約束
一種方式是在命名規(guī)范上加以約束,約定下劃線開頭的屬性是私有屬性。
class Person {
_name = 'name' // 約定下劃線開頭的屬性是私有屬性
getName() {
return this._name // 類內(nèi)部可以訪問私有屬性
}
}
const person = new Person()
person._name // 開發(fā)者可以不遵守命名規(guī)范,運(yùn)行時(shí)在類外部訪問完全沒問題
vue源碼中也有很多地方用下劃線開頭的命名來表示屬性和變量。

但這終究是一種弱約束,運(yùn)行時(shí)完全可以在類外部訪問到這些屬性,沒有任何問題。
基于閉包
function Person(){
const name = 'name'
this.getName = function(){
return name
}
}
const person = new Person()
person.getName() // 'name'
person.name // undefined
上面的代碼getName函數(shù)引用了Person函數(shù)的詞法環(huán)境,利用閉包的特性實(shí)現(xiàn)了私有屬性。私有屬性name在Person外部無法訪問,只能通過特權(quán)方法getName訪問到。
不過這種方式的缺點(diǎn)也很明顯:
私有屬性和特權(quán)方法都只能在構(gòu)造函數(shù)內(nèi)部聲明,而且,這里方法并不是掛載在原型上的,每實(shí)例化一個(gè)對象,就會(huì)生成一次方法。
將私有屬性移動(dòng)到類外部結(jié)合ES模塊
const name = 'name'
export class Person {
getName() {
return name
}
}
上面的代碼,ES模塊僅導(dǎo)出類,不導(dǎo)出類外部的變量name,這樣一來類可以訪問到變量name,而外部則訪問不到。
const person = new Person() person.getName() // 'name' person.name // undefined
基于Symbol
const name = Symbol('name')
export class Person {
[name] = 'name'
getName() {
return this[name]
}
}
上面的代碼用變量存儲(chǔ)了一個(gè)Symbol值,在類內(nèi)部通過動(dòng)態(tài)屬性的方式為類添加了一個(gè)私有屬性。同樣的基于ES模塊僅導(dǎo)出類,而不導(dǎo)出Symbol。這樣在使用的時(shí)候就無法訪問Symbol值聲明的私有屬性了。
const person = new Person() person.getName() // 'name'
但是,其實(shí)還是有辦法獲取到這個(gè)Symbol值的。
const symbols = Object.getOwnPropertySymbols(person) person[symbols[0])
所以,這種方式也并沒有那么私有。
TypeScript中的private
TypeScript中不是有private修飾符嗎,用這個(gè)試試怎么樣呢?
class Person {
private name = 'name'
getName() {
return name
}
}
const person = new Person()
person.getName() // 'name'
person.name // 編譯時(shí)錯(cuò)誤 Property 'name' is private and only accessible within class 'Person'.
在TypeScript中試圖在類外部訪問private屬性會(huì)在編譯時(shí)報(bào)錯(cuò),看起來很美好對吧。但別忘了TypeScript終究要被編譯成JavaScript的,我們來看看編譯結(jié)果:

編譯成JavaScript后,private修飾符沒有了。如果我們通過動(dòng)態(tài)屬性繞過編譯時(shí)的類型檢查,編譯后的JavaScript代碼在運(yùn)行時(shí)并不會(huì)報(bào)錯(cuò):
person['name'] // 'name'

ES2022
ES2022正式引入了私有屬性,在屬性名前加上#來表示私有屬性。
class Person {
#name = 'name'
getName() {
return this.#name
}
}
const person = new Person()
person.getName() // 'name'
person.#name // Uncaught SyntaxError: Private field '#name' must be declared in an enclosing class
不過如果你把上面的代碼放在Chrome控制臺(tái)中執(zhí)行,可能會(huì)發(fā)現(xiàn)person.#name是可以訪問到值的。這是因?yàn)閺腃hrome111開始,開發(fā)者工具里面可以讀寫私有屬性,不會(huì)報(bào)錯(cuò),原因是 Chrome 團(tuán)隊(duì)認(rèn)為這樣方便調(diào)試。
查看MDN了解更多有關(guān)私有屬性的知識(shí)。
WeakMap解決目前的兼容性
如果要考慮ES2022之前的兼容性,還可以用WeakMap來實(shí)現(xiàn)。
const privateFields = new WeakMap()
export class Person {
constructor() {
privateFields.set(this, {
name: 'name'
})
}
getName() {
return privateFields.get(this).name
}
}
上面的代碼在類外部維護(hù)了一個(gè)weakMap,然后在constructor中向weakMap綁定了實(shí)例this和{name: 'name'}的映射關(guān)系。訪問的時(shí)候同樣通過this從weakMap中取出name。
同樣得益于ES模塊的特性,在模塊外部訪問不到weakMap,自然就無法訪問到私有屬性了。
const person = new Person() person.getName() // 'name'
不過這樣的寫法也有缺點(diǎn),就是寫法太繁瑣了,不夠直觀。其實(shí)上面ES2022私有屬性方案在babel編譯后的代碼基本就是和現(xiàn)在類似的方案。

可以看到babel編譯后的代碼變多了很多,因?yàn)橐WC程序的健壯性,必須考慮很多邊緣場景。僅看我紅框框出的代碼也可以看出,編譯后的代碼確實(shí)是采用了WeakMap的方案。
總結(jié)
本文總結(jié)了JavaScript中實(shí)現(xiàn)私有屬性的幾種方式,ES2022引入的私有屬性正式寫法自然是正規(guī)軍,而且寫法也很簡潔。如果要考慮兼容性,WeakMap方案確實(shí)保證了私有性,不過寫法略繁瑣。其余方案或多或少不夠健壯,了解即可。
以上就是JavaScript實(shí)現(xiàn)私有屬性的幾種方式小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript私有屬性的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js前端實(shí)現(xiàn)圖片懶加載(lazyload)的兩種方式
本篇文章主要介紹了js前端實(shí)現(xiàn)圖片懶加載(lazyload)的兩種方式 ,使用圖片懶加載可以提高網(wǎng)頁運(yùn)行速度,有興趣的可以了解一下。2017-04-04
如何快速高效創(chuàng)建JavaScript 二維數(shù)組方法詳解
這篇文章主要為大家介紹了如何快速高效創(chuàng)建JavaScript 二維數(shù)組方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
html5+CSS 實(shí)現(xiàn)禁止IOS長按復(fù)制粘貼功能
因?yàn)樵谝苿?dòng)端APP需要實(shí)現(xiàn)長按執(zhí)行別的事件,但是在iOS系統(tǒng)有默認(rèn)的長按選擇復(fù)制粘貼。禁止在網(wǎng)上找了很多資料,下面小編給大家分享解決方案,一起看看吧2016-12-12
JavaScript使用cookie記錄臨時(shí)訪客信息的方法
這篇文章主要介紹了JavaScript使用cookie記錄臨時(shí)訪客信息的方法,涉及javascript操作cookie的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04
JS實(shí)現(xiàn)靈巧的下拉導(dǎo)航效果代碼
這篇文章主要介紹了JS實(shí)現(xiàn)靈巧的下拉導(dǎo)航效果代碼,通過自定義js插件腳本dropdown.js實(shí)現(xiàn)簡單的下拉導(dǎo)航效果的功能,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08
asp.net下利用js實(shí)現(xiàn)返回上一頁的實(shí)現(xiàn)方法小集
其實(shí)要實(shí)現(xiàn)這個(gè)功能主要還是要用到j(luò)avascript2009-11-11

