亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript實現(xiàn)私有屬性的幾種方式小結(jié)

 更新時間:2024年04月24日 10:56:34   作者:劉同學有點忙  
在JavaScript中,私有屬性是指只能在對象內(nèi)部訪問的屬性,外部無法直接訪問,JavaScript并沒有提供官方的私有屬性的支持,但可以通過一些技巧來模擬實現(xiàn)私有屬性,所以本文給大家總結(jié)了JavaScript實現(xiàn)私有屬性的幾種方式,需要的朋友可以參考下

什么是私有屬性

我們不摳定義,用大白話來說,如果類中的某個屬性只能在類內(nèi)部使用,在類外部(比如通過類的實例)訪問不到,這個屬性就是私有屬性。

我們用下面的代碼舉例說明,當然了,代碼中的屬性并不是私有屬性,只是為了說明私有屬性是怎么一回事:

class Person {
  name = 'name'  // 我們假設(shè)name是一個私有屬性,當然,它現(xiàn)在不是  
  getName() {
    return this.name // 類內(nèi)部可以訪問私有屬性
  }
}
const person = new Person()
person.getName() // 'name'
person.name // 嘗試直接在類外部訪問私有屬性會報錯

如何實現(xiàn)

早期JavaScript并不支持私有屬性,所以只能通過一些變通的方法曲線救國。

基于命名規(guī)范的的弱約束

一種方式是在命名規(guī)范上加以約束,約定下劃線開頭的屬性是私有屬性。

class Person {
  _name = 'name'  // 約定下劃線開頭的屬性是私有屬性
  getName() {
    return this._name // 類內(nèi)部可以訪問私有屬性
  }
}
const person = new Person()
person._name  // 開發(fā)者可以不遵守命名規(guī)范,運行時在類外部訪問完全沒問題

vue源碼中也有很多地方用下劃線開頭的命名來表示屬性和變量。

但這終究是一種弱約束,運行時完全可以在類外部訪問到這些屬性,沒有任何問題。

基于閉包

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)境,利用閉包的特性實現(xiàn)了私有屬性。私有屬性namePerson外部無法訪問,只能通過特權(quán)方法getName訪問到。

不過這種方式的缺點也很明顯:

私有屬性和特權(quán)方法都只能在構(gòu)造函數(shù)內(nèi)部聲明,而且,這里方法并不是掛載在原型上的,每實例化一個對象,就會生成一次方法。

將私有屬性移動到類外部結(jié)合ES模塊

const name = 'name'
export class Person {
  getName() {
    return name
  }
}

上面的代碼,ES模塊僅導出類,不導出類外部的變量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]
  }
}

上面的代碼用變量存儲了一個Symbol值,在類內(nèi)部通過動態(tài)屬性的方式為類添加了一個私有屬性。同樣的基于ES模塊僅導出類,而不導出Symbol。這樣在使用的時候就無法訪問Symbol值聲明的私有屬性了。

const person = new Person()
person.getName() // 'name'

但是,其實還是有辦法獲取到這個Symbol值的。

const symbols = Object.getOwnPropertySymbols(person)
person[symbols[0])

所以,這種方式也并沒有那么私有。

TypeScript中的private

TypeScript中不是有private修飾符嗎,用這個試試怎么樣呢?

class Person {
  private name = 'name'
  getName() {
    return name
  }
}
const person = new Person()
person.getName() // 'name'
person.name // 編譯時錯誤 Property 'name' is private and only accessible within class 'Person'.

在TypeScript中試圖在類外部訪問private屬性會在編譯時報錯,看起來很美好對吧。但別忘了TypeScript終究要被編譯成JavaScript的,我們來看看編譯結(jié)果:

編譯成JavaScript后,private修飾符沒有了。如果我們通過動態(tài)屬性繞過編譯時的類型檢查,編譯后的JavaScript代碼在運行時并不會報錯:

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控制臺中執(zhí)行,可能會發(fā)現(xiàn)person.#name是可以訪問到值的。這是因為從Chrome111開始,開發(fā)者工具里面可以讀寫私有屬性,不會報錯,原因是 Chrome 團隊認為這樣方便調(diào)試。

查看MDN了解更多有關(guān)私有屬性的知識。

WeakMap解決目前的兼容性

如果要考慮ES2022之前的兼容性,還可以用WeakMap來實現(xiàn)。

const privateFields = new WeakMap()
export class Person {
  constructor() {
    privateFields.set(this, {
      name: 'name'
    })
  }
  
  getName() {
    return privateFields.get(this).name
  }
}

上面的代碼在類外部維護了一個weakMap,然后在constructor中向weakMap綁定了實例this{name: 'name'}的映射關(guān)系。訪問的時候同樣通過this從weakMap中取出name。

同樣得益于ES模塊的特性,在模塊外部訪問不到weakMap,自然就無法訪問到私有屬性了。

const person = new Person()
person.getName() // 'name'

不過這樣的寫法也有缺點,就是寫法太繁瑣了,不夠直觀。其實上面ES2022私有屬性方案在babel編譯后的代碼基本就是和現(xiàn)在類似的方案。

可以看到babel編譯后的代碼變多了很多,因為要保證程序的健壯性,必須考慮很多邊緣場景。僅看我紅框框出的代碼也可以看出,編譯后的代碼確實是采用了WeakMap的方案。

總結(jié)

本文總結(jié)了JavaScript中實現(xiàn)私有屬性的幾種方式,ES2022引入的私有屬性正式寫法自然是正規(guī)軍,而且寫法也很簡潔。如果要考慮兼容性,WeakMap方案確實保證了私有性,不過寫法略繁瑣。其余方案或多或少不夠健壯,了解即可。

以上就是JavaScript實現(xiàn)私有屬性的幾種方式小結(jié)的詳細內(nèi)容,更多關(guān)于JavaScript私有屬性的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論