ECMAScript 6:WeakSet 有什么用?

ECMAScript 6: what is WeakSet for?

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 的实际用途是什么?

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

  • 弱link一个对象入集合
  • 从集合中移除一个对象link
  • 检查对象是否已经link编辑到集合

听起来是不是很熟悉?

在某些应用程序中,开发人员可能需要实现一种快速迭代一系列被大量冗余污染的数据的方法但您只想选择尚未处理的数据之前(唯一)。 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();
}

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

it's not being removed from the WeakSet. Why is that?

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

What is the point of adding objects to WeakSets directly?

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

What is the practical use of WeakSet if we cannot even iterate through it nor get the current size?

您只能获得一点信息:集合中是否包含对象(或一般值)?

这在您想要 "tag" 对象而不实际改变它们(在它们上设置 属性)的情况下很有用。许多算法包含某种 "if x was already seen" 条件(JSON.stringify 循环检测可能是一个很好的例子),并且当您使用用户提供的值时,使用 Set/WeakSet 是可取的。 WeakSet 的优点是它的内容可以在你的算法还在 运行ning 时被垃圾收集,所以当你处理很多时它有助于减少内存消耗(甚至防止泄漏)延迟(甚至可能异步)生成的数据。

这是一个非常难的问题。老实说,我不知道 JavaScript so I asked in esdiscuss and got a convincing answer from Domenic.

WeakSets 在 安全验证 方面很有用。如果你想能够隔离出一块JavaScript。它们允许您标记一个对象以表明它属于一组特殊的对象。

假设我有一个 class ApiRequest:

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

  makeRequest() {
    // do work 
  }
}

现在,我正在编写一个 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

等等,请注意,您不能保留 ApiRequest 对象的普通列表或数组,因为那样会阻止它们被垃圾回收。除了闭包之外,任何事情都可以通过 public 方法实现,例如 Object.getOwnPropertyNamesObject.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.

中移动和制作东西

(当然,WeakSet 做的所有事情 WeakMap 将值映射到 true 也可以做,但对于任何 map/set 结构都是如此)

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

WeakSet 是 WeakMap 的简化版,因为您的值始终为布尔值 true。它允许您标记 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.
};

您的控制台可能无法正确显示内容,因为垃圾回收尚未发生。因此,由于该对象未被垃圾回收,因此它会显示该对象仍处于弱集中。

如果你真的想看看一个弱集是否仍然有对某个对象的引用,那么使用 WeakSet.prototype.has() 方法。这个方法,顾名思义 returns 一个 boolean 指示对象是否仍然存在于 weakset.

示例:

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

"weak" set 或 map 在需要保留任意事物集合但不希望它们出现在集合中以防止内存紧张时这些事物被垃圾回收时很有用. (如果垃圾收集 确实 发生,"reaped" 对象将从收集中悄无声息地消失,因此您实际上可以判断它们是否消失了。)

它们非常好,例如,用作后备缓存:"have I already retrieved this record, recently?" 每次您检索某些内容时,将其放入地图,知道JavaScript 垃圾收集器 将负责为您 "trimming the list",并且它会自动响应当前的内存条件(您可以'合理预期)。

唯一的缺点是这些类型不是 "enumerable." 您不能遍历条目列表 – 可能是因为这可能会 "touch" 这些条目,因此无法达到目的。但是,这是一个很小的代价(你 可以 ,如果需要,"code around it")。

让我回答第一部分,尽量避免让你更加困惑。

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

我在这里看到的很多解释的问题是,它们 re-reference 一个变量给另一个对象,或者给它分配一个原始值,然后检查 WeakMap 是否包含该对象或值作为钥匙。当然不是!它从来没有 object/value 作为键!

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