From the translator: this is the second part of the translation of the article about the collection in EcmaScript 6. The first part can be read
here . For various reasons, the translation of the second part was delayed for quite a long time.
Map
Map
(associative array) is a collection of key-value pairs. This is what the
Map
can do:
What is there to complain about? Here are some features that are
not in the ES6 collections, and which, I think, would be useful:
')
- Fixture for defaults like collections.defaultdict in Python.
- The
Map.fromObject(obj)
function, Map.fromObject(obj)
, to make it easier to write associative arrays using the object-literal syntax syntax ( Note: For example, the object-literal syntax in ES is { "test" : 1 }
)
Again, these features are easy to add.
OK. Remember how I started this article by thinking about how the care of JS execution in a browser affects the design of a language and its features? Now we will start talking about it. I have three examples. Here are the first two.
JS differences, part 1: hash tables without hash codes?
There is one useful feature that, as far as I know, collection classes in ES6 do not support at all.
Suppose we have a
Set
of URL objects.
var urls = new Set; urls.add(new URL(location.href));
These two URLs must be interpreted as identical. All fields have the same. But in Javascript these are two different objects, and it’s impossible to overload the notion of language about the equality of objects.
Other languages ​​support this. In Java, Python, Ruby, individual classes can overload equality. In many Scheme implementations, individual hash tables can be created using different concepts of equality. C ++ supports both options.
However, all these mechanisms require users to implement custom hash functions, and all of them expose the system hash function by default. The committee decided not to put on hash codes in Javascript - at least for now - because of open questions about interoperability and security, which is not a reason for such concern in other languages.
JS Differences, Part 2: Surprise! Predictable!
You probably think that deterministic behavior on the part of the computer is not a reason for surprise. But people are often surprised when I tell them that the iteration over the elements in
Map
and
Set
occurs in the order they are added to the collection. She is determined.
We are used to the arbitrariness of certain aspects of hash tables. We learned to accept it. But there are damn good reasons to try to avoid accidents. As I wrote in 2012:
- There is evidence that some programmers find the arbitrary iteration order surprising and at first incomprehensible. [1] [2] [3] [4] [5] [6]
- The order of enumeration of properties is not specified in ECMAScript, however all large implementations are forced to converge on a specific insertion order, for compatibility. Therefore, it is believed that if TC39 does not begin to specify a deterministic iteration order, "the web will do it for us." [7]
- The iteration order of the hash table may reveal portions of the object hash codes. This places a huge responsibility for security on the hash function implementer. For example, the address of the object should not be recovered from the disclosed parts of its hash code (the disclosure of the address of the object to the untrusted ECMAScript code, although it cannot be exploited by the intruders by itself, would be a big security hole).
When it was discussed in February 2012, I
was in an arbitrary order of iteration . I conducted an experiment to prove that tracking the order of insertion would make the hash tables too slow. I wrote a couple of benchmarks in C ++.
The result struck me.This is how we ended up with hash tables that monitor the order of insertion in JS!
Good reasons to use weak collections
In early June 2015, we discussed an example that includes an animation library on JS. We wanted to store a boolean flag for each DOM object, like this:
if (element.isMoving) { smoothAnimations(element); } element.isMoving = true;
Unfortunately, setting expandable properties in a DOM object in this way is a bad idea, as discussed in the article. In that article, we solved the problem using symbols. But can we do the same using
Set
? It might look like this:
if (movingSet.has(element)) { smoothAnimations(element); } movingSet.add(element);
There is only one drawback:
Map
and
Set
have a
strong reference to each key and each value. This means that if the DOM element has been removed from the document, the garbage collector cannot free memory until the element is removed from the
movingSet
. Libraries usually cope with clearing the memory after themselves. This can lead to memory leaks.
ES6 offers an amazing solution for this. Make
movingSet
WeakSet
instead of
Set
. The leak problem is solved!
So, you can solve this problem with weak collections or symbols. What's better? The discussion about the advantages and disadvantages of each method will make this post too long. If you can use one character throughout the life of a web page, then this is probably normal. If you want to use a lot of short-lived characters, this is an alarming sign: consider using
WeakSet
instead to prevent memory leaks.
WeakMap and WeakSet
WeakMap
and
WeakSet
specified to behave in the same way as
Map
and
Set
, but with the following restrictions:
WeakMap
supports only new, .has(), .get(), .set(), .delete()
.WeakSet
supports only new, .has(), .add(), .delete()
.- The values ​​in
WeakSet
and the keys in WeakMap
must be objects.
Notice that none of the weak collections are iterable. You can not get records from the collection except by the corresponding key.
These restrictions allow the garbage collector to collect dead objects from living weak collections. A similar effect can be achieved with weak references or
weak-keyed dictionaries , but weak collections in ES6 have all the advantages of memory management without disclosing the fact that garbage collection has occurred in the scripts.
JS differences, part 3: hiding undetected garbage collector
Under the hood, weak collections are implemented as
ephemeron tables .
In short,
WeakSet
does not keep strong references to objects within itself. When an object in
WeakSet
is collected by the garbage collector, it is simply deleted from
WeakSet
. Same for
WeakMap
. It does not have strong references to any key. If the key is alive, alive and meaningful.
Why all these restrictions? Why not just add weak links in JS?
Again, the committee does not want to disclose non-deterministic behavior to scripts. Weak cross-browser compatibility - the curse of web development. Weak references reveal the details of the implementation of the garbage collector - the very definition of platform-specific arbitrary behavior. Of course, applications should not depend on the details of a specific platform, but weak links make it difficult to understand how you depend on the behavior of the garbage collector in the current browser in which you run your application. It's hard to deal with them.
In contrast, weak collections in ES6 have a more limited, but strong set of features. The fact that a key or a value has been assembled by the garbage collector is not observed, so that applications can in no way depend on it, even by accident.
This is a specific case of how web-specific problems led to an amazing design solution that improved JS as a language.
Where can I use collections?
All four collection classes are supported in Firefox, Chrome, MS Edge and Safari. To support older browsers, use a
polyfill type
es6-collections .
WeakMap
was first implemented in Firefox by Andreas Galom, a former Mozilla CTO. Tom Schuster implemented
WeakSet
. I implemented
Map
and
Set
. Thanks to Tooru Fujisawa for the patches.
Next week, the “Subtleties of ES6” take a two-week break. This series of articles has told a lot, but some of the most powerful features of ES6 will be described. Join us when we come back with new content on July 9th.