中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JS私有屬性的實現方式有哪些

發布時間:2022-03-30 14:10:37 來源:億速云 閱讀:159 作者:小新 欄目:開發技術

這篇文章主要為大家展示了“JS私有屬性的實現方式有哪些”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“JS私有屬性的實現方式有哪些”這篇文章吧。

class 是創建對象的模版,由一系列屬性和方法構成,用于表示對同一概念的數據和操作。

有的屬性和方法是對外的,但也有的是只想內部用的,也就是私有的,那怎么實現私有屬性和方法呢?

不知道大家會怎么實現,我梳理了下,我大概用過 6 種方式,我們分別來看一下:

_prop

區分私有和公有最簡單的方式就是加個下劃線 _,從命名上來區分。

比如:

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。

但是這種方式只是一種命名規范,告訴開發者這個屬性、方法是私有的,不要調用,但終究不是強制的,如果別人要用也阻止不了。

JS私有屬性的實現方式有哪些

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

那怎么能基于這種規范實現真正的私有呢?這就要用到 Proxy 了:

Proxy

Proxy 可以定義目標對象的 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();

我們不直接調用它的對象的屬性方法了,而是先用一層 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: 如果以下劃線 _ 開頭就返回空,否則返回目標對象的屬性值 target[prop]

  • set: 如果以下劃線 _ 開頭就直接返回,否則設置目標對象的屬性值

  • ownKeys: 訪問 keys 時,過濾掉目標對象中下劃線開頭的屬性再返回

這樣就實現了下劃線開頭的屬性的私有化:

我們測試下:

const proxy = new Proxy(dong, handler)

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

JS私有屬性的實現方式有哪些

確實,這里只打印了公有屬性的方法,而下劃線開頭的那兩個屬性沒有打印。

我們基于 _prop 這種命名規范實現了真正的私有屬性!

再調用下方法試試:

JS私有屬性的實現方式有哪些

咋是 undefined 了?

因為 proxy.hello 方法的 this 也是指向 proxy 的,也會受限制,所以要再做下處理:

JS私有屬性的實現方式有哪些

如果用的是方法,那就給它綁定 this 為目標對象。

這樣 hello 方法就可以訪問到那些 _ 開頭的私有屬性了:

JS私有屬性的實現方式有哪些

我們通過 Proxy 給下劃線的命名規范實現了真正的私有屬性,但是要定義一層 Proxy 比較麻煩,有沒有不定義 Prxoy 的方式呢?

確實有,比如 Symbol:

Symbol

Symbol 是 es2015 添加的一個 api,用于創建唯一的值。基于這個唯一的特性,我們就可以實現私有屬性。

比如這樣:

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 生成唯一的值來作為名字。

這樣外面因為拿不到屬性名,就沒法取到對應的屬性值:

JS私有屬性的實現方式有哪些

這種方式比 Proxy 的方式更簡單一些,也是用的很多的一種實現私有屬性的方式。

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

JS私有屬性的實現方式有哪些

但是這種私有屬性是真的沒法訪問么?

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

JS私有屬性的實現方式有哪些

所以說這種方式只是 Object.keys 取不到對應的屬性而已,不如 Proxy 那種方式完善。

那不用 Proxy 的方式,還比有沒有 Symbol 更完善的呢?

那可以試試這種:

WeakMap

外面可以訪問到屬性和方法是因為我們把它掛到了 this 上,那不掛到 this 上外面不就訪問不到了么?

比如用一個 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';
    }
}

我們測試下:

JS私有屬性的實現方式有哪些

這樣貌似可以,但不知道大家有沒有發現其中的問題:

  • 所有對象都用同一個 Map,之間相互影響

  • 對象銷毀了這個 Map 依然存在

怎么解決這個問題呢?

不知道大家用沒用過 WeakMap,它的特性是只能用對象作為 key,對象銷毀,這個鍵值對就銷毀。

完美解決了上面兩個問題:

  • 因為是用對象作為 key 的,那不同的對象是放在不同的鍵值對上的,相互沒影響

  • 對象銷毀的時候,對應的鍵值對就銷毀,不需要手動管理

貌似是很完美,我們實現下:

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';
    }
}

每個屬性定義了一個 WeakMap 來維護,key 為當前對象,值為屬性值,get 和 set 使用 classPrivateFieldSet 和 classPrivateFieldGet 這兩個方法,最終是通過從 WeakMap 中存取的。

在構造器里初始化下當前對象對應的屬性值,也就是 dongName.set(this, void 0),這里的 void 0 的返回值是 undefined,一個意思。

測試下:

JS私有屬性的實現方式有哪些

哇,通過 WeakMap 也能實現私有屬性!

不過這里的 classPrivateFieldGet 沒必要定義吧,直接 xxMap.get 不就行么?

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

但這樣寫起來也很麻煩呀,有沒有更簡單的方式呢?

能不能設計一種語法糖,它自動編譯成這種方式呢?

想的沒錯,確實有這種語法糖:

#prop

現在有一個私有屬性的 es 草案,可以通過 # 的方式來標識私有屬性和方法。

比如這樣:

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 的插件,它可以實現這種語法的編譯:

JS私有屬性的實現方式有哪些

babel 就是把 #prop 編譯成上面那種 WeakMap 的方式來實現的。

這個插件在 @babel/preset-env 的預設里,會自動引入:

JS私有屬性的實現方式有哪些

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

JS私有屬性的實現方式有哪些

也是會編譯成 WeakMap 的方式來實現。

其實 ts 實現的新語法還是不少的,比如 ? 和 ?? 分別是可選鏈和默認值的語法,下面這兩種寫法等價:

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

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

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

其實它是私有屬性但也不完全是,我們來看一下:

ts private

ts 可以通過 private 來修飾屬性、方法的可見性:

  • private 表示屬性私有,只有 class 內部可訪問

  • protected 表示保護,只有 class 和子 class 可訪問

  • public 表示公有,外部也可訪問

類型檢查和提示的時候是有區別的,比如 private 屬性在 class 外部不可訪問:

JS私有屬性的實現方式有哪些

而 class 內部是可以訪問的:

JS私有屬性的實現方式有哪些

但是這種約束只是用于類型檢查的,只存在編譯期間,運行時并沒有這種約束。

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

JS私有屬性的實現方式有哪些

可以看到沒有做任何處理。

而如果用 #prop 的方式,除了編譯時是 private 的,運行時也是:

JS私有屬性的實現方式有哪些

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

總結

class 用于定義圍繞某個概念的一系列屬性和方法,這些屬性和方法有的是內部用的,有的是對外的。只有內部用的屬性、方法需要實現私有化。

實現私有屬性方法,我樹立了 6 種方式:

  • 通過下劃線 _prop 從命名上區分

  • 通過 Proxy 來定義 get、set、ownKeys 的邏輯

  • 通過 Symbol 來定義唯一的屬性名,不能通過 keys 拿到

  • 通過 WeakMap 來保存所有對象的私有屬性和方法

  • 通過 #prop 的 es 新語法實現私有,babel 和 tsc 會把它們編譯成 WeakMap 的方式

  • 通過 ts 的 private 在編譯時約束

這六種方式,有三種只是偽私有,比如 _prop(依然可以訪問)、ts 的 private(運行時可訪問)、Symbol(可以通過 Object.getOwnSymbols 拿到 symbol 來訪問)。

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

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

以上是“JS私有屬性的實現方式有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

js
AI

莒南县| 景宁| 响水县| 耒阳市| 万安县| 南召县| 山东省| 武义县| 涿州市| 乐都县| 诸暨市| 赤水市| 平遥县| 兴业县| 长阳| 肇源县| 化州市| 荣昌县| 马边| 博客| 昔阳县| 阿拉善右旗| 辽中县| 溧阳市| 慈溪市| 漳浦县| 当阳市| 鹰潭市| 吐鲁番市| 会宁县| 玉屏| 蕉岭县| 静乐县| 资中县| 柳河县| 文昌市| 通榆县| 腾冲县| 友谊县| 井研县| 墨江|