【问题标题】:ES6 Set, WeakSet, Map and WeakMapES6 Set、WeakSet、Map 和 WeakMap
【发布时间】:2015-12-19 01:13:31
【问题描述】:

已经有一些关于map和weak map的问题了,像这样:What's the difference between ES6 Map and WeakMap?但是我想问一下我应该在什么情况下使用这些数据结构?或者当我偏爱一个而不是其他时我应该考虑什么?

数据结构示例来自:https://github.com/lukehoban/es6features

// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set

奖金。以上哪种数据结构会产生相同/相似的结果:let hash = object.create(null); hash[index] = something;

【问题讨论】:

标签: javascript ecmascript-6


【解决方案1】:

这在规范的§23.3 中有介绍:

如果被用作WeakMap 键/值对的键的对象只能通过遵循从该WeakMap 开始的引用链访问,则该键/值对不可访问,并且会自动从WeakMap 中删除。

因此,弱映射中的条目,如果它们的键没有被其他任何东西引用,将在某个时候被垃圾收集回收。

相比之下,Map 持有对其键的 strong 引用,如果地图是唯一引用它们的对象,则它们不会被垃圾收集。

MDNputs it like this:

WeakMap 中的密钥被弱化。这意味着,如果没有其他对该键的强引用,那么垃圾收集器将从WeakMap 中删除整个条目。

WeakSet 也是如此。

...我应该在什么情况下使用这种数据结构?

任何你不希望你有一个地图/设置使用一个键来防止该键被垃圾收集的情况。以下是一些示例:

  1. 拥有对实例真正私有的特定于实例的信息,如下所示:(注意:此示例来自 2015 年,远在 private fields 成为一个选项之前。在 2021 年,我会使用私有字段。)

    let Thing = (() => {
        var privateData = new WeakMap();
    
        class Thing {
            constructor() {
                privateData[this] = {
                    foo: "some value"
                };
            }
    
            doSomething() {
                console.log(privateData[this].foo);
            }
        }
    
        return Thing;
    })();
    

    该范围函数之外的代码无法访问privateData 中的数据。该数据由实例本身键入。如果没有WeakMap,您将不会这样做,因为这将是内存泄漏,您的Thing 实例将永远不会被清理。但是WeakMap 只保存 weak 引用,因此如果您的代码使用了 Thing 实例并释放它对实例的引用,WeakMap 不会阻止实例被垃圾收集;相反,实例键入的条目会从映射中删除。

  2. 保存您无法控制的对象的信息。假设您从某个 API 获得一个对象,并且您需要记住有关该对象的一些附加信息。您可以向对象本身添加属性(如果它不是密封的),但是向不受控制的对象添加属性只是自找麻烦。相反,您可以使用对象键入的WeakMap 来存储您的额外信息。

  3. WeakSet 的一个用例是跟踪或标记:假设在“使用”一个对象之前,您需要知道该对象过去是否曾被“使用过”,但无需将其作为标志存储在对象(也许是因为如果它是对象上的标志,其他代码可以看到它[尽管您可以使用私有字段来防止这种情况];或者因为它不是您的对象[所以私有字段无济于事])。例如,这可能是某种一次性访问令牌。 WeakSet 是一种简单的方法,无需强制对象留在内存中。

上述哪种数据结构会产生相同/相似的结果:let hash = Object.create(null); hash[index] = something;

这将最接近Map,因为字符串index(属性名称)将由对象中的强引用保存(如果没有其他引用它,它及其关联的属性将不会被回收)。

【讨论】:

  • 我这周一直在学习 ES6/TypeScript,这一定是我第 30 次遇到并受益于你的答案,它已经到了我现在期待接受答案的地步成为你的。感谢您投入大量时间和精力来帮助这个社区和其他开发者。
  • @LogicalBranch - 天哪,谢谢!我真的很感谢你花时间这么说。 :-)
猜你喜欢
  • 1970-01-01
  • 2013-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-10
  • 1970-01-01
  • 2015-06-07
相关资源
最近更新 更多