一、从垃圾回收章节中知道,JavaScript 引擎在值可访问(并可能被使用)时将其存储在内存中。

    | ⭐️【示例】```javascript let john = { name: “John” };

    // 该对象能被访问,john 是它的引用

    // 覆盖引用 john = null;

    // 该对象将会被从内存中清除

    1. |
    2. | --- |
    3. 二、通常,当对象、数组这类数据结构在内存中时,它们的子元素,如对象的属性、数组的元素都是可以访问的。<br />1、如果把一个对象放入到数组中,那么只要这个数组存在,那么这个对象也就存在,即使没有其他对该对象的引用。
    4. | ⭐️【示例】```javascript
    5. let john = { name: "John" };
    6. let array = [ john ];
    7. john = null; // 覆盖引用
    8. // 前面由 john 所引用的那个对象被存储在了 array 中
    9. // 所以它不会被垃圾回收机制回收

    | | —- |

    2、类似的,如果我们使用对象作为常规Map的键,那么当Map存在时,该对象也将存在。它会占用内存,并且应该不会被(垃圾回收机制)回收。

    | ⭐️【示例】```javascript let john = { name: “John” };

    let map = new Map(); map.set(john, “…”);

    john = null; // 覆盖引用

    // john 被存储在了 map 中, // 我们可以使用 map.keys() 来获取它

     |
    | --- |
    
    三、WeakMap在这方面有着根本上的不同。它不会阻止垃圾回收机制对作为键的对象(key object)的回收。
    
    <a name="QEc4m"></a>
    # WeakMap
    一、WeakMap对象也是键值对的集合。它的键必须是对象类型,值可以是任意类型。<br />1、它的键被弱保持,也就是说,当其键所指对象没有其他地方引用的时候,它会被GC回收掉。
    
    | ⭐️【示例】```javascript
    let weakMap = new WeakMap();
    
    let obj = {};
    
    weakMap.set(obj, "ok"); // 正常工作(以对象作为键)
    weakMap.set(['test'], 'Whoops'); // 正常工作
    
    // 不能使用字符串作为键
    weakMap.set("test", "Whoops"); // Error,因为 "test" 不是一个对象
    

    | | —- |

    2、WeakMap是类似于Map的集合,它仅允许对象作为键,并且一旦通过其他方式无法访问它们,便会将它们与其关联值一同删除。
    二、如果我们在 weakMap 中使用一个对象作为键,并且没有其他对这个对象的引用 —— 该对象将会被从内存(和map)中自动清除。

    | ⭐️【示例】```javascript let john = { name: “John” };

    let weakMap = new WeakMap(); weakMap.set(john, “…”);

    john = null; // 覆盖引用

    // john 被从内存中删除了!

    1、与上面常规的Map的例子相比,现在如果john仅仅是作为WeakMap的键而存在 —— 它将会被从 map(和内存)中自动删除。 |
    | --- |
    
    三、WeakMap不支持迭代,WeakMap的键是不可枚举的,不提供列出其键的方法(如keys(),values()和entries()方法)。所以没有办法获取WeakMap的所有键或值。<br />1、列表是否存在取决于垃圾回收器的状态,是不可预知的。<br />四、WeakMap只有以下的方法:
    
    - weakMap.get(key)
    - weakMap.set(key, value)
    - weakMap.delete(key)
    - weakMap.has(key)
    
    五、为什么会有这种限制呢?这是技术的原因。如果一个对象丢失了其它所有引用(就像上面示例中的john),那么它就会被垃圾回收机制自动回收。但是在从技术的角度并不能准确知道何时会被回收。<br />1、这些都是由 JavaScript 引擎决定的。JavaScript 引擎可能会选择立即执行内存清理,如果现在正在发生很多删除操作,那么 JavaScript 引擎可能就会选择等一等,稍后再进行内存清理。因此,从技术上讲,WeakMap的当前元素的数量是未知的。JavaScript 引擎可能清理了其中的垃圾,可能没清理,也可能清理了一部分。因此,暂不支持访问WeakMap的所有键/值的方法。
    <a name="Gjeup"></a>
    # WeakMap使用案例
    <a name="ZYQV0"></a>
    ## 额外的数据
    一、WeakMap的主要应用场景是额外数据的存储。
    
    - 存储与另一对象相关的数据
    - 处理用户访问计数的代码
    | 【示例】假如我们正在处理一个“属于”另一个代码的一个对象,也可能是第三方库,并想存储一些与之相关的数据,那么这些数据就应该与这个对象共存亡 —— 这时候WeakMap正是我们所需要的利器。<br />1、我们将这些数据放到WeakMap中,并使用该对象作为这些数据的键,那么当该对象被垃圾回收机制回收后,这些数据也会被自动清除。```javascript
    weakMap.set(john, "secret documents");
    // 如果 john 消失,secret documents 将会被自动清除
    

    | | —- |

    | 【示例】例如,我们有用于处理用户访问计数的代码。收集到的信息被存储在 map 中:一个用户对象作为键,其访问次数为值。当一个用户离开时(该用户对象将被垃圾回收机制回收),这时我们就不再需要他的访问次数了。
    1、下面是一个使用Map的计数函数的例子:```javascript // 📁 visitsCount.js let visitsCountMap = new Map(); // map: user => visits count

    // 递增用户来访次数 function countUser(user) { let count = visitsCountMap.get(user) || 0; visitsCountMap.set(user, count + 1); }

    (1)下面是其他部分的代码,可能是使用它的其它代码:```javascript
    // 📁 main.js
    let john = { name: "John" };
    
    countUser(john); // count his visits
    
    // 不久之后,john 离开了
    john = null;
    

    (2)现在john这个对象应该被垃圾回收,但他仍在内存中,因为它是visitsCountMap中的一个键。
    (3)当我们移除用户时,我们需要清理visitsCountMap,否则它将在内存中无限增大。在复杂的架构中,这种清理会成为一项繁重的任务。
    2、我们可以通过使用WeakMap来避免这样的问题:```javascript // 📁 visitsCount.js let visitsCountMap = new WeakMap(); // weakmap: user => visits count

    // 递增用户来访次数 function countUser(user) { let count = visitsCountMap.get(user) || 0; visitsCountMap.set(user, count + 1); }

    (1)现在我们不需要去清理visitsCountMap了。当john对象变成不可访问时,即便它是WeakMap里的一个键,它也会连同它作为WeakMap里的键所对应的信息一同被从内存中删除。 |
    | --- |
    
    
    <a name="ax2OL"></a>
    ## 缓存
    一、另外一个普遍的例子是缓存:当一个函数的结果需要被记住(“缓存”),这样在后续的对同一个对象的调用时,就可以重用这个被缓存的结果。
    
    | 【示例】1、我们可以使用Map来存储结果,就像这样:```javascript
    // 📁 cache.js
    let cache = new Map();
    
    // 计算并记住结果
    function process(obj) {
      if (!cache.has(obj)) {
        let result = /* calculations of the result for */ obj;
    
        cache.set(obj, result);
      }
    
      return cache.get(obj);
    }
    
    // 现在我们在其它文件中使用 process()
    
    // 📁 main.js
    let obj = {/* 假设我们有个对象 */};
    
    let result1 = process(obj); // 计算完成
    
    // ……稍后,来自代码的另外一个地方……
    let result2 = process(obj); // 取自缓存的被记忆的结果
    
    // ……稍后,我们不再需要这个对象时:
    obj = null;
    
    alert(cache.size); // 1(啊!该对象依然在 cache 中,并占据着内存!)
    

    (1)对于多次调用同一个对象,它只需在第一次调用时计算出结果,之后的调用可以直接从cache中获取。这样做的缺点是,当我们不再需要这个对象的时候需要清理cache。
    2、如果我们用WeakMap替代Map,这个问题便会消失:当对象被垃圾回收时,对应的缓存的结果也会被自动地从内存中清除。```javascript // 📁 cache.js let cache = new WeakMap();

    // 计算并记结果 function process(obj) { if (!cache.has(obj)) { let result = / calculate the result for / obj;

    cache.set(obj, result);
    

    }

    return cache.get(obj); }

    // 📁 main.js let obj = {/ some object /};

    let result1 = process(obj); let result2 = process(obj);

    // ……稍后,我们不再需要这个对象时: obj = null;

    // 无法获取 cache.size,因为它是一个 WeakMap, // 要么是 0,或即将变为 0 // 当 obj 被垃圾回收,缓存的数据也会被清除

     |
    | --- |
    
    | 【示例】深拷贝时判断是否循环引用:[深拷贝](https://www.yuque.com/webfront/tu3uvd/dlth8w?view=doc_embed) |
    | --- |
    
    
    <a name="Nx7B6"></a>
    ## 存储一个对象的私有数据或隐藏实施细节
    一、WeakMap对象的一个用例是存储一个对象的私有数据或隐藏实施细节。<br />1、对象内部的私有数据和方法被存储在WeakMap类型的privates变量中。<br />2、所有暴露出的原型和情况都是公开的,而其他内容都是外界不可访问的,因为模块并未导出privates对象。
    
    | 【示例】```javascript
    const privates = new WeakMap();
    
    function Public() {
      const me = {
        // Private data goes here
      };
      privates.set(this, me);
    }
    
    Public.prototype.method = function () {
      const me = privates.get(this);
      // Do stuff with private data in `me`...
    };
    
    module.exports = Public;
    

    | | —- |