In the next article, I’m going to talk about WeakMap WeakSet, which most people don’t write in code

The promise in my last post, “JavaScript Interview Questions beyond [this],” has come true.


Before we talk about WeakSet, let’s review Set, a relatively simple data structure.

A Set is a collection of values, and you can iterate over its elements in the order in which they were inserted. The elements in a Set occur only once, that is, the elements in a Set are unique.

It provides the following interface:

  • Set.prototype.addAdds an element to a collection
  • Set.prototype.deleteRemoves an element from a collection
  • Set.prototype.hasDetermines whether an element exists in the set
  • Set.prototype.clearEmpty the collection
  • Set.prototype.entries/forEach/valuesYou iterate through this set in a variety of different ways

Then singled out WeakSet for comparison

WeakSet objects allow you to store weakhold objects in a collection.

First, the references in a WeakSet are weak references. This point is also described in detail in MDN.

WeakSet holds weak reference: The references of objects in the collection are weak references. If there are no other references to the objects in the WeakSet, those objects will be garbage collected. This also means that there is no list of current objects stored in the WeakSet. Because of this, WeakSet is not enumerable.

Also because of non-enumeration, so in the Set involved in the traversal method in the WeakSet does not exist. Therefore, WeakSet interface is set so several

  • WeakSet.prototype.addAdds an element to a collection
  • WeakSet.prototype.deleteRemoves an element from a collection
  • WeakSet.prototype.hasDetermines whether an element exists in the set

Both Add and DELETE are interfaces that modify the internal of WeakSet. The only really useful interface is to judge whether the element exists in the set — has.

That makes WeakSet look weak. We actually use collection type data structures, and actually the most common operations are going to be for traversal, whether it’s an Array or a Set.

We use Set instead of Array because the scenario requires an unordered Set with unique values. We thought WeakSet could be used to solve the situation where an unordered, one-value set with weak references is needed. However, the result is very different, the goods can not be traversable, how can be used as a collection.


If you want to turn MDN, actually can see a small episode — a abandoned interface weakset.prototype. Clear

Clearly similar to set.prototype. clear this is an interface for clearing, nothing special. It doesn’t matter if it is deprecated, because all cases where you can use clear can be replaced by creating a new instance with new WeakSet().

But what’s interesting is this one

There is no specification or draft. This method was originally planned tobe included in ECMAScript 6, but was abandoned in draft Revision 28 (October 14, 2014). The original implementation of the browser was removed soon after, and it was never part of the standard.

Yes, this interface was originally included in ES6 and was already implemented, but it was abandoned. As for the reasons, the above description of weak references is already beginning to appear.

This also means that there is no list of current objects stored in the WeakSet.

It doesn’t even have a list of the current objects and of course there’s no way to “empty” them. At the same time, it also confirms our previous judgment — this thing is not designed to be like a Set at all, it is not designed to be used as a Set in a particular scene.


Before we move on to what WeakSet is actually used for, there’s one more interesting thing to discuss.

Some people see weak reference, think of garbage collection, and then began to study the JS engine to realize such a powerful function, must be changed a lot of the underlying architecture what…

Interestingly, however, there were polyfills that could simulate this functionality when WeakSet first came out.

Write a few lines of code to show how…

export class WeakSet {
    #id = ' '

    constructor() {
        this.#id = Symbol()}add(obj) {
        Object.defineProperty(obj, this.#id, {
            enumable: false.configurable: true})},delete(obj) {
        if (obj.hasOwnProperty(this.#id)) {
            delete obj[this.#id]
        }
    }

    has(obj) {
        return obj.hasOwnProperty(this.#id)
    }
    
    clear(){
        this.#id = Symbol()}}Copy the code

In short, we simply create an unenumerable attribute on the object to indicate whether the element exists in the current WeakSet.

If it is a WeakMap, it can directly write the corresponding value into the property.


This polyfill makes things more interesting. It means that WeakMap WeakSet has an equivalent writing.

// A simple class that has a private property of val and a corresponding set/get
class Foo {
    #val = null
    
    setVal(v) { this.#val = v }
    getVal(v) { return this.#val }
}

// Using WeakMap, val is split from the inside of the object, and the corresponding set/get method becomes static
class Bar {
    static map = new WeakMap(a)static getVal(obj) {
       if( Bar.map.has(obj)){
           return Bar.map.get(obj)
       }else{
           return null}}static setVal(obj,v){
        Bar.map.set(obj,v)
    }
}
Copy the code

This equivalent writing method shows that where we use a WeakMap, we can directly write it as a property to this object, which conversely means that a property on an object can be split to the external through a WeakMap.

And that’s what a WeakMap WeakSet is for.


The value is written on the object, and the code can’t run, so why bother to break it up into an external data structure?

Can run does not mean good writing!!

When writing business, you may encounter less, in fact, if you write some architectural modules, it is not difficult to find this scenario:

I need to extend some of the objects, such as recording some log information, you can add properties in the original encapsulated classes, add interfaces. The trouble is that classes get bloated, and you can’t guarantee that other modules won’t use them in the future, preventing you from refactoring this functionality. What’s more, some objects come from third-party libraries and cannot be changed.

Of course, it’s possible to add fields without changing the class, but code that breaks encapsulation can sometimes lead to unexpected situations, such as passing in a freeze object.

Use WeakMap WeakSet, not only can not destroy the original code encapsulation, but also can control the visibility of these data, why not?


Where the previous article discussed the well-known this, this article discusses the lesser-known WeakMap WeakSet

The two articles discuss very different things, but they actually want to express the same meaning. For some language features or third-party libraries, memorizing the interface or even scratching the source code sentence by sentence will not make it useful. Understanding the design intention behind the interface is the key.

Next post, I’m going to write code on the hour, if you think this article has some content, please kindly give me a thumbs up.