欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解JS私有屬性的6種實(shí)現(xiàn)方式

 更新時(shí)間:2022年03月30日 12:05:14   作者:zxg_神說(shuō)要有光  
class是創(chuàng)建對(duì)象的模版,由一系列屬性和方法構(gòu)成,用于表示對(duì)同一概念的數(shù)據(jù)和操作。有的屬性和方法是對(duì)外的,但也有的是私有的。本文梳理了六種私有屬性的實(shí)現(xiàn)方式,需要的可以參考一下

class 是創(chuàng)建對(duì)象的模版,由一系列屬性和方法構(gòu)成,用于表示對(duì)同一概念的數(shù)據(jù)和操作。

有的屬性和方法是對(duì)外的,但也有的是只想內(nèi)部用的,也就是私有的,那怎么實(shí)現(xiàn)私有屬性和方法呢?

不知道大家會(huì)怎么實(shí)現(xiàn),我梳理了下,我大概用過(guò) 6 種方式,我們分別來(lái)看一下:

_prop

區(qū)分私有和公有最簡(jiǎn)單的方式就是加個(gè)下劃線 _,從命名上來(lái)區(qū)分。

比如:

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();

console.log(dong.hello());

這里的 Dong 就有私有屬性 _name、_age,公有屬性 friend。

但是這種方式只是一種命名規(guī)范,告訴開(kāi)發(fā)者這個(gè)屬性、方法是私有的,不要調(diào)用,但終究不是強(qiáng)制的,如果別人要用也阻止不了。

不過(guò)這種方式用的還是挺多的,歷史比較悠久。

那怎么能基于這種規(guī)范實(shí)現(xiàn)真正的私有呢?這就要用到 Proxy 了:

Proxy

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

比如還是這個(gè) 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)用它的對(duì)象的屬性方法了,而是先用一層 Proxy 來(lái)約束下 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)

我們通過(guò) new Proxy 來(lái)給 dong 定義了 get、set、ownKeys 的 handler:

  • get: 如果以下劃線 _ 開(kāi)頭就返回空,否則返回目標(biāo)對(duì)象的屬性值 target[prop]
  • set: 如果以下劃線 _ 開(kāi)頭就直接返回,否則設(shè)置目標(biāo)對(duì)象的屬性值
  • ownKeys: 訪問(wèn) keys 時(shí),過(guò)濾掉目標(biāo)對(duì)象中下劃線開(kāi)頭的屬性再返回

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

我們測(cè)試下:

const proxy = new Proxy(dong, handler)

for (const key of Object.keys(proxy)) {
    console.log(key, proxy[key])
}

確實(shí),這里只打印了公有屬性的方法,而下劃線開(kāi)頭的那兩個(gè)屬性沒(méi)有打印。

我們基于 _prop 這種命名規(guī)范實(shí)現(xiàn)了真正的私有屬性!

再調(diào)用下方法試試:

咋是 undefined 了?

因?yàn)?proxy.hello 方法的 this 也是指向 proxy 的,也會(huì)受限制,所以要再做下處理:

如果用的是方法,那就給它綁定 this 為目標(biāo)對(duì)象。

這樣 hello 方法就可以訪問(wèn)到那些 _ 開(kāi)頭的私有屬性了:

我們通過(guò) Proxy 給下劃線的命名規(guī)范實(shí)現(xiàn)了真正的私有屬性,但是要定義一層 Proxy 比較麻煩,有沒(méi)有不定義 Prxoy 的方式呢?

確實(shí)有,比如 Symbol:

Symbol

Symbol 是 es2015 添加的一個(gè) api,用于創(chuàng)建唯一的值?;谶@個(gè)唯一的特性,我們就可以實(shí)現(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 生成唯一的值來(lái)作為名字。

這樣外面因?yàn)槟貌坏綄傩悦?,就沒(méi)法取到對(duì)應(yīng)的屬性值:

這種方式比 Proxy 的方式更簡(jiǎn)單一些,也是用的很多的一種實(shí)現(xiàn)私有屬性的方式。

如果想暴露出去,可以定義個(gè) get 方法:

但是這種私有屬性是真的沒(méi)法訪問(wèn)么?

不是的,有一個(gè) api 叫做 Object.getOwnPropertySymbols,可以取到對(duì)象的所有 Symbols 屬性,然后就可以拿到屬性值了:

所以說(shuō)這種方式只是 Object.keys 取不到對(duì)應(yīng)的屬性而已,不如 Proxy 那種方式完善。

那不用 Proxy 的方式,還比有沒(méi)有 Symbol 更完善的呢?

那可以試試這種:

WeakMap

外面可以訪問(wèn)到屬性和方法是因?yàn)槲覀儼阉鼟斓搅?this 上,那不掛到 this 上外面不就訪問(wèn)不到了么?

比如用一個(gè) Map 保存私有屬性:

const privateFields = new Map();

class Dong {
    constructor() {
        privateFields.set('name', 'dong');
        privateFields.set('age', 20);
    }

    hello() {
        return 'I\'m ' + privateFields.get('name') + ', '  + privateFields.get('name') + ' years old';
    }
}

我們測(cè)試下:

這樣貌似可以,但不知道大家有沒(méi)有發(fā)現(xiàn)其中的問(wèn)題:

  • 所有對(duì)象都用同一個(gè) Map,之間相互影響
  • 對(duì)象銷(xiāo)毀了這個(gè) Map 依然存在

怎么解決這個(gè)問(wèn)題呢?

不知道大家用沒(méi)用過(guò) WeakMap,它的特性是只能用對(duì)象作為 key,對(duì)象銷(xiāo)毀,這個(gè)鍵值對(duì)就銷(xiāo)毀。

完美解決了上面兩個(gè)問(wèn)題:

  • 因?yàn)槭怯脤?duì)象作為 key 的,那不同的對(duì)象是放在不同的鍵值對(duì)上的,相互沒(méi)影響
  • 對(duì)象銷(xiāo)毀的時(shí)候,對(duì)應(yīng)的鍵值對(duì)就銷(xiāo)毀,不需要手動(dòng)管理

貌似是很完美,我們實(shí)現(xiàn)下:

const dongName = new WeakMap();
const dongAge = new WeakMap();

const classPrivateFieldSet = function(receiver, state, value) {
    state.set(receiver, value);
}

const classPrivateFieldGet = function(receiver, state) {
    return state.get(receiver);
}


class Dong {
    constructor() {
        dongName.set(this, void 0);
        dongAge.set(this, void 0);

        classPrivateFieldSet(this, dongName, 'dong');
        classPrivateFieldSet(this, dongAge, 20);
    }

    hello() {
        return 'I\'m ' + classPrivateFieldGet(this, dongName) + ', '  + classPrivateFieldGet(this, dongAge) + ' years old';
    }
}

每個(gè)屬性定義了一個(gè) WeakMap 來(lái)維護(hù),key 為當(dāng)前對(duì)象,值為屬性值,get 和 set 使用 classPrivateFieldSet 和 classPrivateFieldGet 這兩個(gè)方法,最終是通過(guò)從 WeakMap 中存取的。

在構(gòu)造器里初始化下當(dāng)前對(duì)象對(duì)應(yīng)的屬性值,也就是 dongName.set(this, void 0),這里的 void 0 的返回值是 undefined,一個(gè)意思。

測(cè)試下:

哇,通過(guò) WeakMap 也能實(shí)現(xiàn)私有屬性!

不過(guò)這里的 classPrivateFieldGet 沒(méi)必要定義吧,直接 xxMap.get 不就行么?

確實(shí),包一層的目的是為了可以加一些額外的邏輯,這里也可以直接從 weakMap 取。

但這樣寫(xiě)起來(lái)也很麻煩呀,有沒(méi)有更簡(jiǎn)單的方式呢?

能不能設(shè)計(jì)一種語(yǔ)法糖,它自動(dòng)編譯成這種方式呢?

想的沒(méi)錯(cuò),確實(shí)有這種語(yǔ)法糖:

#prop

現(xiàn)在有一個(gè)私有屬性的 es 草案,可以通過(guò) # 的方式來(lái)標(biāo)識(shí)私有屬性和方法。

比如這樣:

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 是公有的。

這種新語(yǔ)法 JS 引擎沒(méi)那么快支持,但是可以通過(guò) babel 或者 ts 編譯器來(lái)編譯成低版本語(yǔ)法的方式來(lái)提前用。

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

babel 就是把 #prop 編譯成上面那種 WeakMap 的方式來(lái)實(shí)現(xiàn)的。

這個(gè)插件在 @babel/preset-env 的預(yù)設(shè)里,會(huì)自動(dòng)引入:

除了 babel,ts 里也可以直接用這種語(yǔ)法:

也是會(huì)編譯成 WeakMap 的方式來(lái)實(shí)現(xiàn)。

其實(shí) ts 實(shí)現(xiàn)的新語(yǔ)法還是不少的,比如 ? 和 ?? 分別是可選鏈和默認(rèn)值的語(yǔ)法,下面這兩種寫(xiě)法等價(jià):

const res = data?.name ?? 'dong';
const res2 = data && data.name  || 'dong';

這種新語(yǔ)法都是直接可用的,babel 的話需要引入下 proposal 插件。

對(duì)了,我記得 ts 里 class 也是有 private 的修飾符的,那個(gè)不也是私有屬性么?

其實(shí)它是私有屬性但也不完全是,我們來(lái)看一下:

ts private

ts 可以通過(guò) private 來(lái)修飾屬性、方法的可見(jiàn)性:

  • private 表示屬性私有,只有 class 內(nèi)部可訪問(wèn)
  • protected 表示保護(hù),只有 class 和子 class 可訪問(wèn)
  • public 表示公有,外部也可訪問(wèn)

類(lèi)型檢查和提示的時(shí)候是有區(qū)別的,比如 private 屬性在 class 外部不可訪問(wèn):

而 class 內(nèi)部是可以訪問(wèn)的:

但是這種約束只是用于類(lèi)型檢查的,只存在編譯期間,運(yùn)行時(shí)并沒(méi)有這種約束。

我們可以看下編譯后的代碼:

可以看到?jīng)]有做任何處理。

而如果用 #prop 的方式,除了編譯時(shí)是 private 的,運(yùn)行時(shí)也是:

所以,要實(shí)現(xiàn)真正的 private 的話,還是用 #prop 的方式,如果只是編譯時(shí)約束那聲明下 private 就行。

總結(jié)

class 用于定義圍繞某個(gè)概念的一系列屬性和方法,這些屬性和方法有的是內(nèi)部用的,有的是對(duì)外的。只有內(nèi)部用的屬性、方法需要實(shí)現(xiàn)私有化。

實(shí)現(xiàn)私有屬性方法,我樹(shù)立了 6 種方式:

  • 通過(guò)下劃線 _prop 從命名上區(qū)分
  • 通過(guò) Proxy 來(lái)定義 get、set、ownKeys 的邏輯
  • 通過(guò) Symbol 來(lái)定義唯一的屬性名,不能通過(guò) keys 拿到
  • 通過(guò) WeakMap 來(lái)保存所有對(duì)象的私有屬性和方法
  • 通過(guò) #prop 的 es 新語(yǔ)法實(shí)現(xiàn)私有,babel 和 tsc 會(huì)把它們編譯成 WeakMap 的方式
  • 通過(guò) ts 的 private 在編譯時(shí)約束

這六種方式,有三種只是偽私有,比如 _prop(依然可以訪問(wèn))、ts 的 private(運(yùn)行時(shí)可訪問(wèn))、Symbol(可以通過(guò) Object.getOwnSymbols 拿到 symbol 來(lái)訪問(wèn))。

另外三種是真正的私有,包括 Proxy、WeakMap、#prop(目前是編譯為 WeakMap 的方式)。

有的是從屬性名上想辦法,比如 _prop 和 Symbol,有的是從 this 上想辦法,比如 Proxy(包一層) 和 WeakMap(不掛到 this),有的是從語(yǔ)言本身想辦法,比如 ts 的 private 或者 es 新語(yǔ)法的 #prop。

這 6 種實(shí)現(xiàn)私有屬性的方式,你用過(guò)幾種?

以上就是一文詳解JS私有屬性的6種實(shí)現(xiàn)方式的詳細(xì)內(nèi)容,更多關(guān)于JS私有屬性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • javascript判斷網(wǎng)頁(yè)是關(guān)閉還是刷新

    javascript判斷網(wǎng)頁(yè)是關(guān)閉還是刷新

    本篇文章給大家介紹js判斷網(wǎng)頁(yè)是關(guān)閉還是刷新,實(shí)現(xiàn)原理就是通過(guò)離開(kāi)頁(yè)面行為時(shí)間onunload觸發(fā)時(shí)間去檢測(cè)此時(shí)的瀏覽器的窗口大小,根據(jù)大小由此判斷用戶是刷新,跳轉(zhuǎn)或是關(guān)閉行為程序,需要的朋友可以參考下本文
    2015-09-09
  • 微信小程序?qū)崿F(xiàn)表單驗(yàn)證提交功能

    微信小程序?qū)崿F(xiàn)表單驗(yàn)證提交功能

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)表單驗(yàn)證提交功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • JS中setTimeout()的用法詳解

    JS中setTimeout()的用法詳解

    setTimeout( ) 是屬于 window 的 method, 但我們都是略去 window 這頂層物件名稱(chēng), 這是用來(lái)設(shè)定一個(gè)時(shí)間, 時(shí)間到了, 就會(huì)執(zhí)行一個(gè)指定的 method
    2013-04-04
  • 關(guān)于js new Date() 出現(xiàn)NaN 的分析

    關(guān)于js new Date() 出現(xiàn)NaN 的分析

    在一個(gè)項(xiàng)目中需要進(jìn)行日期的格式化,后臺(tái)傳到前端是時(shí)間的整數(shù)(Date.getTime),當(dāng)后臺(tái)數(shù)據(jù)返回字符串時(shí),發(fā)現(xiàn)轉(zhuǎn)換日期時(shí)在ie下變成NaN,但是真的是這樣嗎?接下來(lái)我們慢慢分析
    2012-10-10
  • 純js實(shí)現(xiàn)倒計(jì)時(shí)功能

    純js實(shí)現(xiàn)倒計(jì)時(shí)功能

    本文主要介紹了通過(guò)js實(shí)現(xiàn)頁(yè)面的倒計(jì)時(shí)功能的思路與方法,具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • 鼠標(biāo)拖動(dòng)動(dòng)態(tài)改變表格的寬度的js腳本 兼容ie/firefox

    鼠標(biāo)拖動(dòng)動(dòng)態(tài)改變表格的寬度的js腳本 兼容ie/firefox

    table拖動(dòng)(兼容Firefox 3.5/IE6),固定表格寬度在網(wǎng)上搜索了好久,找到的都是只能在IE下有效的,后來(lái)終于找到了支持firefox的了。
    2009-12-12
  • JavaScript簡(jiǎn)單實(shí)現(xiàn)彈出拖拽窗口(二)

    JavaScript簡(jiǎn)單實(shí)現(xiàn)彈出拖拽窗口(二)

    這篇文章再次為大家詳細(xì)介紹了JavaScript簡(jiǎn)單實(shí)現(xiàn)彈出拖拽窗口的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Javascript 同時(shí)提交多個(gè)Web表單的方法

    Javascript 同時(shí)提交多個(gè)Web表單的方法

    1 問(wèn)題來(lái)自一位網(wǎng)友的提問(wèn): web頁(yè)面里有多個(gè)表單,每個(gè)表單對(duì)應(yīng)著某一類(lèi)數(shù)據(jù)操作。
    2009-02-02
  • 淺談JS中的bind方法與函數(shù)柯里化

    淺談JS中的bind方法與函數(shù)柯里化

    下面小編就為大家?guī)?lái)一篇淺談JS中的bind方法與函數(shù)柯里化。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • 微信小程序使用擴(kuò)展組件庫(kù)WeUI的入門(mén)教程

    微信小程序使用擴(kuò)展組件庫(kù)WeUI的入門(mén)教程

    WeUI是一套同微信原生視覺(jué)體驗(yàn)一致的基礎(chǔ)樣式庫(kù),由微信官方設(shè)計(jì)團(tuán)隊(duì)為微信內(nèi)網(wǎng)頁(yè)和微信小程序量身設(shè)計(jì),令用戶的使用感知更加統(tǒng)一,下面這篇文章主要給大家介紹了關(guān)于微信小程序使用擴(kuò)展組件庫(kù)WeUI的相關(guān)資料,需要的朋友可以參考下
    2022-04-04

最新評(píng)論