<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ?JavaScript私有屬性的多種實現(xiàn)方式總匯

          共 4395字,需瀏覽 9分鐘

           ·

          2022-04-07 21:17

          點擊上方?前端Q,關(guān)注公眾號

          回復(fù)加群,加入前端Q技術(shù)交流群


          來源 | http://www.fly63.com

          JavaScript被很多人認(rèn)為并不是一種面向?qū)ο笳Z言,原因有很多種,比如JavaScript沒有類,不能提供傳統(tǒng)的類式繼承;再比如JavaScript不能實現(xiàn)信息的隱藏,不能實現(xiàn)私有成員。

          本文并不是為了打破以上誤解(實際上筆者自己也有困惑),只是簡單介紹幾種JavaScript實現(xiàn)私有屬性的方式,以及各自的優(yōu)劣。

          1、基于編碼規(guī)范約定實現(xiàn)方式

          很多編碼規(guī)范把以下劃線_開頭的變量約定為私有成員,便于同團隊開發(fā)人員的協(xié)同工作。實現(xiàn)方式如下:

          function Person(name){  this._name = name;}
          var person = new Person('Joe');

          這種方式只是一種規(guī)范約定,很容易被打破。而且也并沒有實現(xiàn)私有屬性,上述代碼中的實例person可以直接訪問到_name屬性:

          alert(person._name); //'Joe'

          2、?基于閉包的實現(xiàn)方式

          另外一種比較普遍的方式是利用JavaScript的閉包特性。構(gòu)造函數(shù)內(nèi)定義局部變量和特權(quán)函數(shù),其實例只能通過特權(quán)函數(shù)訪問此變量,如下:

          function Person(name){  var _name = name;  this.getName = function(){    return _name;  }}
          var person = new Person('Joe');

          這種方式的優(yōu)點是實現(xiàn)了私有屬性的隱藏,Person 的實例并不能直接訪問_name屬性,只能通過特權(quán)函數(shù)getName獲?。?/span>

          alert(person._name); // undefinedalert(person.getName()); //'Joe'

          使用閉包和特權(quán)函數(shù)實現(xiàn)私有屬性的定義和訪問是很多開發(fā)者采用的方式。但是這種方式存在一些缺陷:

          • 私有變量和特權(quán)函數(shù)只能在構(gòu)造函數(shù)中創(chuàng)建。通常來講,構(gòu)造函數(shù)的功能只負責(zé)創(chuàng)建新對象,方法應(yīng)該共享于prototype上。特權(quán)函數(shù)本質(zhì)上是存在于每個實例中的,而不是prototype上,增加了資源占用。

          3、?基于強引用散列表的實現(xiàn)方式

          JavaScript不支持Map數(shù)據(jù)結(jié)構(gòu),所謂強引用散列表方式其實是Map模式的一種變體。簡單來講,就是給每個實例新增一個唯一的標(biāo)識符,以此標(biāo)識符為key,對應(yīng)的value便是這個實例的私有屬性,這對key-value保存在一個Object內(nèi)。實現(xiàn)方式如下:

          var Person = (function() {
          var privateData = {}, privateId = 0;
          function Person(name) { Object.defineProperty(this, "_id", { value: privateId++ });
          privateData[this._id] = { name: name }; }
          Person.prototype.getName = function() { return privateData[this._id].name; };
          return Person;}());

          上述代碼的有以下幾個特征:

          1. 使用自執(zhí)行函數(shù)創(chuàng)建Person類,變量privateData和privateId被所有實例共享;

          2. privateData用來儲存每個實例的私有屬性name的key-value,privateId用來分配每個實例的唯一標(biāo)識符_id;

          3. 方法getName存在于prototype上,被所有實例共享。

          這種方式在目前ES5環(huán)境下,基本是最佳方案了。但是仍然有一個致命的缺陷:散列表privateData對每個實例都是強引用,導(dǎo)致實例不能被垃圾回收處理。如果存在大量實例必然會導(dǎo)致memory leak。

          造成以上問題的本質(zhì)是JavaScript的閉包引用,以及只能使用字符串類型最為散列表的key值。針對這兩個問題,ES6新增的WeakMap可以良好的解決。

          4、基于WeakMap的實現(xiàn)方式

          WeakMap有以下特點:

          1)、支持使用對象類型作為key值;

          2)、弱引用。

          根據(jù)WeakMap的特點,便不必為每個實例都創(chuàng)建一個唯一標(biāo)識符,因為實例本身便可以作為WeakMap的key。改進后的代碼如下:

          var Person = (function() {
          var privateData = new WeakMap();
          function Person(name) { privateData.set(this, { name: name }); }
          Person.prototype.getName = function() { return privateData.get(this).name; };
          return Person;}());

          改進的代碼不僅僅干凈了很多,而且WeakMap是一種弱引用散列表, 這意味著,如果沒有其他引用和該鍵引用同一個對象,這個對象將會被當(dāng)作垃圾回收掉。解決了內(nèi)存泄露的問題。

          不幸的是,目前瀏覽器對WeakMap的支持率并不理想,投入生產(chǎn)環(huán)境仍然需要等待。

          5、基于Proxy約束

          Proxy 可以定義目標(biāo)對象的 get、set、Object.keys 的邏輯,可以在這一層做一下判斷,如果是下劃線 _ 開頭就不讓訪問,否則就可以訪問。

          比如還是這個 class:

          class Dong {    constructor() {        this._name = 'dong';        this._age = 20;        this.friend = 'guang';    }    hello() {        return 'I\'m ' + this._name + ', '  + this._age + ' years old';    }}const dong = new Dong();

          我們不直接調(diào)用它的對象的屬性方法了,而是先用一層 Proxy 來約束下 get、set、getKeys 的行為:

          const dong = new Dong();const handler = {    get(target, prop) {        if (prop.startsWith('_')) {            return;        }        return target[prop];   },   set(target, prop, value) {    if (prop.startsWith('_')) {        return;     }     target[prop] = value;   },   ownKeys(target, prop) {      return Object.keys(target).filter(key => !key.startsWith('_'))   }, }const proxy = new Proxy(dong, handler)

          我們通過 new Proxy 來給 dong 定義了 get、set、ownKeys 的 handler:

          • get:?如果以下劃線 _ 開頭就返回空,否則返回目標(biāo)對象的屬性值 target[prop]。

          • set:?如果以下劃線 _ 開頭就直接返回,否則設(shè)置目標(biāo)對象的屬性值。

          • ownKeys:?訪問 keys 時,過濾掉目標(biāo)對象中下劃線開頭的屬性再返回。

          這樣就實現(xiàn)了下劃線開頭的屬性的私有化:

          我們測試下:

          const proxy = new Proxy(dong, handler)
          for (const key of Object.keys(proxy)) { console.log(key, proxy[key])}

          確實,這里只打印了共有屬性的方法,而下劃線開頭的那兩個屬性沒有打印。我們基于 _prop 這種命名規(guī)范實現(xiàn)了真正的私有屬性!

          6、Symbol用于創(chuàng)建唯一的值

          Symbol 是 es2015 添加的一個 api,用于創(chuàng)建唯一的值?;谶@個唯一的特性,我們就可以實現(xiàn)私有屬性。

          比如這樣:

          const nameSymbol = Symbol('name');const ageSymbol = Symbol('age');class Dong {    constructor() {        this[nameSymbol] = 'dong';        this[ageSymbol] = 20;    }    hello() {        return 'I\'m ' + this[nameSymbol] + ', '  + this[ageSymbol] + ' years old';    }}const dong = new Dong();

          我們不再用 name 和 age 作為私有屬性名了,而是用 Symbol 生成唯一的值來作為名字。

          7、es新草案 #prop

          現(xiàn)在有一個私有屬性的 es 草案,可以通過 # 的方式來標(biāo)識私有屬性和方法。

          比如這樣:

          class Dong {    constructor() {        this.#name = 'dong';        this.#age = 20;        this.friend = 'guang';    }    hello() {        return 'I\'m ' + this.#name + this.#age + 'years old';    }}

          這里的 name 和 age 都是私有的,而 friend 是共有的。

          這種新語法 JS 引擎沒那么快支持,但是可以通過 babel 或者 ts 編譯器來編譯成低版本語法的方式來提前用。

          比如 babel 有 @babel/proposal-private-property-in-object 的插件,它可以實現(xiàn)這種語法的編譯。


          往期推薦


          秒啊!答好這5個問題,就入門Docker了
          Vue3!煥然一新的 Vue3 中文文檔來了!
          深入講解VsCode各場景高級調(diào)試與使用技巧

          最后


          • 歡迎加我微信,拉你進技術(shù)群,長期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個專業(yè)的技術(shù)人...

          點個在看支持我吧
          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青娱乐cao | 精品操| 中文字幕第18页 | 超碰免费中文字幕 | 亚洲第一大成人网站 |