JavaScript >> Javascript 文檔 >  >> Tags >> map

JavaScript 中帶有弱映射的私有實例成員

上週我看到一篇文章 1 Nick Fitzgerald 在其中描述了一種使用 ECMAScript 6 弱映射為 JavaScript 類型創建私有實例成員的方法。老實說,我從來都不是弱圖的大力支持者——我認為沒有什麼大驚小怪的,而且它們只有一個用例(跟踪與 DOM 元素相關的數據)。直到我讀到尼克的文章,我仍然堅持這個信念,那時我的弱圖信念系統爆炸了。我現在看到了weakmaps給JavaScript帶來的可能性,以及它們將如何以我們可能還無法完全想像的方式改變我們的編碼實踐。除了尼克提到的,這是本文的重點。

私有成員的遺產

JavaScript 的最大缺點之一是無法在自定義類型上創建真正私有的實例成員。唯一好的方法是在構造函數內部創建私有變量並創建訪問它們的特權方法,例如:

function Person(name) {
    this.getName = function() {
        return name;
    };
}

在本例中,getName() 方法使用 name 參數(實際上是一個局部變量)來返回人的姓名,而不會暴露 name 作為財產。如果您有大量 Person,這種方法還可以,但效率非常低 實例,因為每個都必須攜帶自己的 getName() 副本 而不是在原型上共享一個方法。

或者,您可以選擇按照慣例將成員設為私有,就像許多人在成員名稱前加上下劃線一樣。下劃線不是魔法,它不會阻止任何人使用該成員,而是提醒人們不要使用某些東西。例如:

function Person(name) {
    this._name = name;
}

Person.prototype.getName = function() {
    return this._name;
};

這裡的模式更有效,因為每個實例都將在原型上使用相同的方法。然後該方法訪問 this._name ,它也可以在對象之外訪問,但我們都同意不這樣做。這不是一個理想的解決方案,但它是許多開發人員依賴於某種保護措施的解決方案。

還有跨實例共享成員的情況,使用包含構造函數的立即調用函數表達式 (IIFE) 很容易創建。例如:

var Person = (function() {

    var sharedName;

    function Person(name) {
        sharedName = name;
    }

    Person.prototype.getName = function() {
        return sharedName;
    };

    return Person;
}());

這裡,sharedNamePerson 的所有實例之間共享 ,並且每個新實例都會用 name 覆蓋該值 這是傳入的。這顯然是一個荒謬的例子,但卻是了解如何獲取真正私有成員的重要的第一步。

面向真正的私人會員

共享私有成員的模式指向了一個潛在的解決方案:如果私有數據沒有存儲在實例上但實例可以訪問它怎麼辦?如果有一個對象可以與實例的所有私人信息一起隱藏起來怎麼辦。在 ECMAScript 6 之前,你會這樣:

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

現在我們正在取得進展。 privateData 對象無法從 IIFE 外部訪問,完全隱藏了其中包含的所有數據。 privateId 變量存儲實例可以使用的下一個可用 ID。不幸的是,該 ID 需要存儲在實例上,因此最好確保它不能以任何方式更改,因此使用 Object.defineProperty() 設置其初始值並確保該屬性不可寫、不可配置或不可枚舉。保護 _id 從被篡改。然後,在 getName() 裡面 , 該方法訪問 _id 從私有數據存儲中獲取適當的數據並返回。

除了那個醜陋的殘留 _id 之外,這種方法是解決實例私有數據問題的一個很好的方法 附加到實例上。即使實例被垃圾收集,這也存在永久保留所有數據的問題。然而,這種模式是我們用 ECMAScript 5 能做到的最好的模式。

進入弱圖

通過在圖片中添加弱圖,前面示例的“幾乎但不完全”的性質就消失了。 Weakmaps 解決了私有數據成員的剩餘問題。首先,不需要有唯一的 ID,因為對象實例是唯一的 ID。其次,當一個對象實例被垃圾回收時,weakmap 中與該實例相關的所有數據也將被垃圾回收。可以使用與上一個示例相同的基本模式,但現在更簡潔:

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

privateData 在這個例子中是 WeakMap 的一個實例 .當一個新的 Person 創建後,在弱映射中為實例創建一個條目以保存包含私有數據的對象。弱圖中的key是this ,即使對於開發人員來說獲得對 Person 的引用是微不足道的 對象,無法訪問 privateData 在實例之外,因此數據可以安全地遠離麻煩製造者。任何想要操作私有數據的方法都可以通過傳入 this 來獲取給定實例的適當數據 並查看返回的對象。在本例中,getName() 檢索對象並返回 name 屬性。

結論

我會以我開始的方式結束:我對弱圖的看法是錯誤的。我現在明白為什麼人們對它們如此興奮,如果我只是將它們用於創建真正私有(且非 hacky)的實例成員,那麼我會覺得我的錢物有所值。我要感謝 Nick Fitzgerald 的帖子啟發了我寫這篇文章,並讓我看到了弱圖的可能性。我可以很容易地預見到我將使用弱映射作為我日常 JavaScript 工具包的一部分的未來,我焦急地等待著我們可以跨瀏覽器使用它們的那一天。

參考

  1. Nick Fitzgerald (fitzgeraldnick.com) 使用 ECMAScript 6 WeakMaps 隱藏實現細節

Tutorial JavaScript 教程
  1. 揭開長箭頭運算符的神秘面紗

  2. GoodFil.ms Angular.JS 的聯合創始人

  3. 如果它 indexOf null 從數組中的一行中刪除行

  4. 在我寫博客的前 20 天裡,我如何將我的博客流量增加到超過 29,000 次頁面瀏覽量

  5. 如何使用 $.ajax()

  6. 適合新手的哈希表

  7. 如果沒有前端框架,您將如何製作全棧應用程序?

  1. 使用 NativeScript 進行特定於平台的開發

  2. ⚡️ 如何在 Vue.js 中調用基於 OAuth 的 API?

  3. MS Teams 開發初學者指南 #1:選項卡

  4. 翻譯 JS 應用程序的更好方法

  5. 將 Bootstrap CSS 框架添加到 Angular 應用程序

  6. 你永遠看不到你的臉

  7. [視頻] Addy Osmani 的 JavaScript 現狀

  1. Node 中流的美妙之處

  2. JavaScript:執行上下文和詞法範圍

  3. Next.js 入門,一個教程

  4. 如何創建具有 0 個依賴項的 React Toasts/Notifications