ECMAScript 6:什么是WeakSet?

Rob*_*bok 52 javascript ecmascript-6 traceur

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);
Run Code Online (Sandbox Code Playgroud)

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

WeakSet {[1, 2, 3], Object {name: "Charlie"}}
WeakSet {[1, 2, 3], Object {name: "Charlie"}}
Run Code Online (Sandbox Code Playgroud)

这是为什么?

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

weakset.add({name: "Charlie"});
Run Code Online (Sandbox Code Playgroud)

那些Traceur的故障还是我错过了什么?

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

Ber*_*rgi 53

它没有从WeakSet中删除.这是为什么?

很可能是因为垃圾收集器还没有运行.但是,你说你正在使用Traceur,所以它可能是因为它们没有得到适当的支持.我想知道怎么console能显示出来的内容WeakSet.

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

绝对没有必要向WeakSets 添加对象文字.

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

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

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

  • 感谢您使用"标记"一词 - 我现在理解了整个观点.迭代和WeakSets中的`.size`也不可用,因为它们不可靠?垃圾收集器可以随时工作,因此信息不会有任何用处.但是`.has()`是不同的故事,因为如果我们有任何引用将它传递给`.has()`方法,垃圾收集器肯定没有清理它.我的理解合理吗?:) (14认同)
  • @RoboRobok:确切说:-)一个可以让`WeakSet`实现,是可迭代的,但那些可能会导致不确定性的算法,如果以错误的方式使用(根据GC行为),因此ES委员会决定不做可用的内容. (3认同)

Ben*_*aum 28

这是一个非常难的问题.说实话,我不知道在JavaScript的背景下,所以我在es escucuss中询问并得到了Domenic的令人信服的答案.

WeakSet对安全性验证有用.如果你想能够隔离一段JavaScript.它们允许您标记对象以指示它属于一组特殊对象.

假设我有一节课ApiRequest:

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

  makeRequest() {
    // do work 
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,我正在编写一个JavaScript平台 - 我的平台允许您运行JavaScript来进行调用 - 进行您需要的调用ApiRequest- 我只希望您ApiRequest使用我给您的对象来创建,这样您就无法绕过任何约束我有位置.

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

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
Run Code Online (Sandbox Code Playgroud)

等等,请注意您不能保留正常的列表或ApiRequest对象数组,因为这样可以防止它们被垃圾回收.除了关闭之外,任何事情都可以用Object.getOwnPropertyNames或等公共方法来实现Object.getOwnSymbols.所以你一个人,我做了:

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

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

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

简而言之 - WeakMaps对于在JavaScirpt中编写平台非常有用.通常这种验证是在C++方面完成的,但添加这些功能将允许在JavaScript中移动和制作内容.

(当然,映射值的所有内容WeakSet也可以做,但对于任何map/set构造都是如此)WeakMaptrue

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

  • @ssube如果可以进行多次`makeRequest`调用怎么办?如果`ApiRequest`实例通过但有时根本不使用(0调用)怎么办?它必须弱的原因是不将请求的生命周期绑定到集合. (2认同)

Tao*_*oPR 11

根据定义,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();
}
Run Code Online (Sandbox Code Playgroud)

上面应用程序的最佳数据结构之一是Bloom过滤器,它非常适合海量数据.但是,您也可以将WeakSet用于此目的.

  • 请注意,在插入对象后,“processedBag.has(nextObject)”将始终返回 true,因为通过引用它,您将阻止 GC 收集它。**换句话说,如果您在 var 超出范围时不需要弱集的自动删除功能,您也可以使用普通集**。 (2认同)

Mik*_*son 5

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

例如,它们非常适合用作后备缓存:“我最近是否已经检索过此记录?” 每次你检索到一些东西时,把它放入地图中,知道JavaScript 垃圾收集器将负责为你“修剪列表”,并且它会自动这样做以响应普遍的内存条件(你可以' t 合理预期)。

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