72

WeakSet 应该通过弱引用来存储元素。也就是说,如果一个对象没有被其他任何东西引用,则应该从 WeakSet 中清除它。

我写了以下测试:

var weakset = new WeakSet(),
    numbers = [1, 2, 3];

weakset.add(numbers);
weakset.add({name: "Charlie"});

console.log(weakset);

numbers = undefined;

console.log(weakset);

即使我的[1, 2, 3]数组没有被任何东西引用,它也不会从 WeakSet 中删除。控制台打印:

WeakSet {[1, 2, 3], Object {name: "Charlie"}}
WeakSet {[1, 2, 3], Object {name: "Charlie"}}

这是为什么?

另外,我还有一个问题。直接将对象添加到 WeakSets 有什么意义,如下所示:

weakset.add({name: "Charlie"});

是 Traceur 的故障还是我遗漏了什么?

最后,如果我们甚至无法遍历它也无法获得当前大小,那么 WeakSet 的实际用途是什么?

4

7 回答 7

76

它不会从 WeakSet 中删除。这是为什么?

很可能是因为垃圾收集器尚未运行。但是,您说您正在使用 Traceur,因此可能是它们没有得到适当的支持。我想知道无论如何console可以显示 a 的内容WeakSet

直接将对象添加到 WeakSets 有什么意义?

WeakSet向s添加对象字面量绝对没有意义。

如果我们甚至无法遍历它也无法获得当前大小,那么 WeakSet 的实际用途是什么?

你所能得到的只是一点信息:对象(或一般来说,值)是否包含在集合中?

这在您想要“标记”对象而不实际改变它们(在它们上设置属性)的情况下很有用。许多算法包含某种“如果x已经看到”条件(JSON.stringify循环检测可能是一个很好的例子),并且当您使用用户提供的值时,建议使用Set/ WeakSet。a here 的优点WeakSet是它的内容可以在您的算法仍在运行时被垃圾收集,因此当您处理大量惰性数据(甚至可能是异步的)时,它有助于减少内存消耗(甚至防止泄漏)产生。

于 2015-05-31T10:13:56.197 回答
37

这是一个非常难的问题。老实说,我对 JavaScript 的上下文一无所知,所以我在 esdiscuss 中询问并从Domenic那里得到了令人信服的答案。

出于安全验证原因,WeakSets 很有用。如果你希望能够隔离一段 JavaScript。它们允许您标记对象以表明它属于一组特殊的对象。

假设我有一堂课ApiRequest

class ApiRequest {
  constructor() {
    // bring object to a consistent state, use platform code you have no direct access to
  }

  makeRequest() {
    // do work 
  }
}

现在,我正在编写一个 JavaScript 平台——我的平台允许你运行 JavaScript 来进行调用——进行那些你需要的调用 a——ApiRequest我只希望你ApiRequest使用我给你的对象进行 s,这样你就不能绕过任何约束我已经到位。

但是,目前没有什么能阻止您这样做:

ApiRequest.prototype.makeRequest.call(null, args); // make request as function
Object.create(ApiRequest.prototype).makeRequest(); // no initialization
function Foo(){}; Foo.prototype = ApiRequest.prototype; new Foo().makeRequest(); // no super

依此类推,请注意,您不能保留普通的ApiRequest对象列表或数组,因为这会阻止它们被垃圾收集。除了闭包之外,任何事情都可以通过诸如Object.getOwnPropertyNamesor之类的公共方法来实现Object.getOwnSymbols。所以你和我一起做:

const requests = new WeakSet();
class ApiRequest {
  constructor() {
    requests.add(this);
  }

  makeRequest() {
    if(!request.has(this)) throw new Error("Invalid access");
    // do work
  }
}

现在,无论我做什么——我必须持有一个有效的ApiRequest对象来调用makeRequest它的方法。如果没有 WeakMap/WeakSet,这是不可能的。

所以简而言之 - WeakMaps 对于用 JavaScript 编写平台很有用。通常这种验证是在 C++ 端完成的,但添加这些功能将启用 JavaScript 中的移动和制作。

(当然,将值映射到的所有 aWeakSet都可以执行,但对于任何 map/set 构造都是如此)WeakMaptrue

(就像 Bergi 的回答所暗示的,没有理由直接将对象文字添加到 aWeakMap或 a WeakSet

于 2015-06-02T16:05:34.920 回答
14

根据定义,WeakSet只有三个关键功能

  • 将对象弱链接到集合中
  • 从集中删除对象的链接
  • 检查对象是否已链接到集合

听起来更熟悉?

在某些应用程序中,开发人员可能需要实现一种快速的方法来遍历被大量冗余污染的一系列数据,但您只想选择之前未处理过的数据(唯一)。WeakSet 可以帮助你。请参见下面的示例:

var processedBag = new WeakSet();
var nextObject = getNext();
while (nextObject !== null){
    // Check if already processed this similar object?
    if (!processedBag.has(nextObject)){
        // If not, process it and memorize 
        process(nextObject);
        processedBag.add(nextObject);
    }
    nextObject = getNext();
}

上述应用的最佳数据结构之一是布隆过滤器,它非常适合海量数据。但是,您也可以为此目的使用Wea​​kSet 。

于 2015-05-31T10:13:07.903 回答
5

当您需要保留任意的事物集合但不希望它们在集合中的存在阻止这些事物在内存紧张时被垃圾收集时,“弱”集合或映射很有用。(如果确实发生了垃圾收集,“收获”的对象将从收集中悄然消失,因此您实际上可以判断它们是否消失了。)

例如,它们非常适合用作后备缓存:“我最近是否已经检索过这条记录?” 每次您检索某些内容时,将其放入映射中,知道JavaScript 垃圾收集器将负责为您“修剪列表”,并且它会自动响应当前的内存条件(您可以) t 合理预期)。

唯一的缺点是这些类型不是“可枚举的”。您不能遍历条目列表——可能是因为这可能会“触及”这些条目并因此破坏目的。但是,这是一个很小的代价(如果需要,您可以“围绕它编写代码”)。

于 2018-11-06T18:07:59.867 回答
1

WeakSet 是 WeakMap 的简化版,因为您的值始终为布尔值。它允许您标记 JavaScript 对象,以便只对它们执行一次操作或保持它们在特定进程中的状态。理论上,因为它不需要保存一个值,所以它应该使用更少的内存并且比 WeakMap 执行得稍微快一些。

var [touch, untouch] = (() => {
    var seen = new WeakSet();
    return [
        value => seen.has(value)) || (seen.add(value), !1),
        value => !seen.has(value) || (seen.delete(value), !1)
    ];
})();

function convert(object) {
    if(touch(object)) return;
    extend(object, yunoprototype); // Made up.
};

function unconvert(object) {
    if(untouch(object)) return;
    del_props(object, Object.keys(yunoprototype)); // Never do this IRL.
};
于 2016-06-07T14:24:45.303 回答
1

由于垃圾收集尚未发生,您的控制台可能错误地显示了内容。因此,由于该对象没有被垃圾回收,它会显示该对象仍处于弱集状态。

如果您真的想查看弱集是否仍然具有对某个对象的引用,请使用该WeakSet.prototype.has()方法。顾名思义,此方法返回一个boolean指示对象是否仍然存在于弱集中的值。

例子:

var weakset = new WeakSet(),
    numbers = [1, 2, 3];

weakset.add(numbers);
weakset.add({name: "Charlie"});

console.log(weakset.has(numbers));

numbers = undefined;

console.log(weakset.has(numbers));

于 2018-11-06T17:39:51.353 回答
0

让我回答第一部分,并尽量避免进一步混淆您。

取消引用对象的垃圾收集是不可观察的!这将是一个悖论,因为您需要一个对象引用来检查它是否存在于地图中。但不要相信我,相信凯尔辛普森: https ://github.com/getify/You-Dont-Know-JS/blob/1st-ed/es6%20%26%20beyond/ch5.md#weakmaps

我在这里看到的很多解释的问题是,他们将一个变量重新引用到另一个对象,或者为其分配一个原始值,然后检查 WeakMap 是否包含该对象或值作为键。当然不是!它从来没有将该对象/值作为键!

所以这个难题的最后一块:为什么在控制台中检查 WeakMap 仍然会显示所有这些对象,即使您已经删除了对这些对象的所有引用?因为控制台本身保持对这些对象的持久引用,以便能够列出 WeakMap 中的所有键,因为这是 WeakMap 本身无法做到的事情。

于 2020-10-05T00:20:12.383 回答