ECMAScript 6: what is WeakSet for?

2019-01-08 14:48发布

The WeakSet is supposed to store elements by weak reference. That is, if an object is not referenced by anything else, it should be cleaned from the WeakSet.

I have written the following test:

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

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

console.log(weakset);

numbers = undefined;

console.log(weakset);

Even though my [1, 2, 3] array is not referenced by anything, it's not being removed from the WeakSet. The console prints:

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

Why is that?

Plus, I have one more question. What is the point of adding objects to WeakSets directly, like this:

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

Are those Traceur's glitches or am I missing something?

And finally, what is the practical use of WeakSet if we cannot even iterate through it nor get the current size?

6条回答
三岁会撩人
2楼-- · 2019-01-08 15:15

Your console was probably incorrectly showing the contents due to the fact that the garbage collection did not take place yet. Therefore since the object wasn't garbage collected it would show the object still in weakset.

If you really want to see if a weakset still has a reference to a certain object then use the WeakSet.prototype.has() method. This method, as the name implies returns a boolean indicating wether the object still exists in the weakset.

Example:

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

查看更多
相关推荐>>
3楼-- · 2019-01-08 15:20

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

Most likely because the garbage collector has not yet run. However, you say you are using Traceur, so it just might be that they're not properly supported. I wonder how the console can show the contents of a WeakSet anyway.

What is the point of adding objects to WeakSets directly?

There is absolutely no point of adding object literals to WeakSets.

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

All you can get is one bit of information: Is the object (or generically, value) contained in the set?

This can be useful in situations where you want to "tag" objects without actually mutating them (setting a property on them). Lots of algorithms contain some sort of "if x was already seen" condition (a JSON.stringify cycle detection might be a good example), and when you work with user-provided values the use of a Set/WeakSet would be advisable. The advantage of a WeakSet here is that its contents can be garbage-collected while your algorithm is still running, so it helps to reduce memory consumption (or even prevents leaks) when you are dealing with lots of data that is lazily (possibly even asynchronously) produced.

查看更多
孤傲高冷的网名
4楼-- · 2019-01-08 15:21

This is a really hard question. To be completely honest I had no idea in the context of JavaScript so I asked in esdiscuss and got a convincing answer from Domenic.

WeakSets are useful for security and validation reasons. If you want to be able to isolate a piece of JavaScript. They allow you to tag an object to indicate it belongs to a special set of object.

Let's say I have a class ApiRequest:

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

  makeRequest() {
    // do work 
  }
}

Now, I'm writing a JavaScript platform - my platform allows you to run JavaScript to make calls - to make those calls you need a ApiRequest - I only want you to make ApiRequests with the objects I give you so you can't bypass any constraints I have in place.

However, at the moment nothing is stopping you from doing:

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

And so on, note that you can't keep a normal list or array of ApiRequest objects since that would prevent them from being garbage collected. Other than a closure, anything can be achieved with public methods like Object.getOwnPropertyNames or Object.getOwnSymbols. So you one up me and do:

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

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

Now, no matter what I do - I must hold a valid ApiRequest object to call the makeRequest method on it. This is impossible without a WeakMap/WeakSet.

So in short - WeakMaps are useful for writing platforms in JavaScirpt. Normally this sort of validation is done on the C++ side but adding these features will enable moving and making things in JavaScript.

(Of course, everything a WeakSet does a WeakMap that maps values to true can also do, but that's true for any map/set construct)

(Like Bergi's answer suggests, there is never a reason to add an object literal directly to a WeakMap or a WeakSet)

查看更多
迷人小祖宗
5楼-- · 2019-01-08 15:22

A "weak" set or map is useful when you need to keep an arbitrary collection of things but you don't want their presence in the collection from preventing those things from being garbage-collected if memory gets tight. (If garbage collection does occur, the "reaped" objects will silently disappear from the collection, so you can actually tell if they're gone.)

They are excellent, for example, for use as a look-aside cache: "have I already retrieved this record, recently?" Each time you retrieve something, put it into the map, knowing that the JavaScript garbage collector will be the one responsible for "trimming the list" for you, and that it will automatically do so in response to prevailing memory conditions (which you can't reasonably anticipate).

The only drawback is that these types are not "enumerable." You can't iterate over a list of entries – probably because this would likely "touch" those entries and so defeat the purpose. But, that's a small price to pay (and you could, if need be, "code around it").

查看更多
Summer. ? 凉城
6楼-- · 2019-01-08 15:24

WeakSet is a simplification of WeakMap for where your value is always going to be boolean true. It allows you to tag JavaScript objects so to only do something with them once or to maintain their state in respect to a certain process. In theory as it doesn't need to hold a value it should use a little less memory and perform slightly faster than 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.
};
查看更多
爷的心禁止访问
7楼-- · 2019-01-08 15:29

By definition, WeakSet has only three key functionalities

  • Weakly link an object into the set
  • Remove a link to an object from the set
  • Check if an object has already been linked to the set

Sounds more pretty familiar?

In some application, developers may need to implement a quick way to iterate through a series of data which is polluted by lots and lots of redundancy but you want to pick only ones which have not been processed before (unique). WeakSet could help you. See an example below:

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

One of the best data structure for application above is Bloom filter which is very good for a massive data size. However, you can apply the use of WeakSet for this purpose as well.

查看更多
登录 后发表回答