WeakMap and WeakSet
JavaScript WeakMap and WeakSet (JavaScript WeakMap和WeakSet)
In chapter Garbage Collection, it was stated that the JavaScript engine can store a value in memory once it is reachable. (在第章“垃圾回收”中指出, JavaScript引擎可以在内存中存储可访问的值。)
Here is an example:
let js = {
name: "Javascript"
};
// object can be accessed, js is the reference to it
// rewrite the reference
js = null;
// the object will be deleted from memory
console.log(js);
As a rule, properties or elements of data structures such as an object or an array are reachable and kept in memory once that data structure is in memory. For example, after putting an object into an array, it will exist as long as the array exists. (通常,一旦数据结构在内存中,对象或数组等数据结构的属性或元素就可以访问并保存在内存中。 例如,将对象放入数组后,只要数组存在,它就会存在。)
Here is an example:
let js = {
name: "Javascript"
};
let array = [js];
js = null; // rewrite the reference
// js is stored in an array, so it will not garbage-collected
// it can be received as an array[0]
console.log(js);
Correspondingly, applying an object like the key in a regular Map, it will exist as long as the map exists. (相应地,在常规地图中应用像键这样的对象,只要地图存在,它就会存在。)
An example will look as follows:
let js = {
name: "Javascript"
};
let map = new Map();
map.set(js, "...");
js = null; // rewriting the reference
// js is on the map,
// it can be received by using map.keys()
console.log(js);
Further, we are going to cover WeakMap, which is completely different and doesn’t restrain from garbage-collection of the key objects. (此外,我们将介绍WeakMap ,它完全不同,并且不会限制关键对象的垃圾收集。)
WeakMap
WeakMap
The primary difference between Map and WeakMap is that the WeakMap key can’t be primitive values. They must be objects, like in the example below:
let weakMap = new WeakMap();
let obj = {};
key1 = weakMap.set(obj, "ok"); // works fine, object key
console.log(key1);
// can't use string as key
key2 = weakMap.set("test", "Oops"); // Error because the “test” is not an object
console.log(key2);
Iteration and keys(), values(), entries() methods are not supported by WeakMap. (WeakMap不支持迭代和keys ()、values ()、entries ()方法。)
The methods, supported by WeakMap are the following: weakMap.get(key), weakMap.set(key, value), weakMap.has(key), and weakMap.delete(key).
weakMap.delete(key)
weakMap.delete(key) (weakMap.delete (key))
Additional data storage is the primary area application for WeakMap. WeakMap is especially useful for storing data associated with a third-party library. For example, consider putting the data into a WeakMap, with an object as the key. Once the object is garbage collected, the data will also automatically vanish as shown below:
let weakMap = new WeakMap();
let obj = {
name: "test"
};
key = weakMap.set(obj, "test docs");
// if obj disappears, test docs will be automatically destroyed
Now, imagine having a code that maintains a visit count for the users. The information is kept inside a map, with a user object as a key and the visit count as the value. (现在,想象一下,有一个代码可以维护用户的访问次数。信息保存在地图中,以用户对象为键,访问计数为值。)
After a user leaves, you intend to stop storing their visit count. (用户离开后,您打算停止存储其访问次数。)
First, let’s see an example of counting function with Map. It will look as follows:
// visitsCount.js
let visitsCountMap = new Map(); // map: book => visits count
// increase the visits count
function countBook(book) {
let count = visitsCountMap.get(book) || 0;
visitsCountMap.set(book, count + 1);
console.log(count);
}
// main.js
let js = {
name: "Javascript"
};
countBook(js); // count his visits
countBook(js); // count his visits
// later js leaves us
js = null;
console.log(js);
So, while removing the users, it is needed to clean visitsCountMap. In another way, it will be stored in memory indefinitely. (因此,在删除用户时,需要清除visitsCountMap。换句话说,它将无限期地存储在内存中。)
However, cleaning like this might become an annoying task sometimes. If you want to avoid it, you can apply to WeakMap. Here is an example of using WeakMap instead of that kind of cleaning:
// visitsCount.js
let visitsCountMap = new WeakMap(); // weakmap: book => visits count
// increase the visits count
function countBook(book) {
let count = visitsCountMap.get(book) || 0;
visitsCountMap.set(book, count + 1);
console.log(count);
}
// main.js
let js = {
name: "Javascript"
};
countBook(js); // count his visits
countBook(js); // count his visits
// later js leaves us
js = null;
console.log(js);
Now, there is no necessity to clean visitsCountMap. (现在,无需清洁visitsCountMap。)
About Caching
About Caching (关于缓存)
Caching happens when a function result must be remembered (cached) to reuse it later while calling on the same object. (当必须记住(缓存)函数结果以便稍后在调用同一对象时重用它时,就会发生缓存。)
Map can be used for storing results as follows:
// cache.js
let cache = new Map();
// calculate and remember the result
function process(myObj) {
if (!cache.has(myObj)) {
let res = /* result calculations for */ myObj;
cache.set(myObj, res);
}
return cache.get(myObj);
}
// Now we use process() in another file:
// main.js
let myObj = { /* let's say we have an object */ };
let res1 = process(myObj); // calculated
// later from another place in the code
let res2 = process(myObj); // the result taken from the cache is remembered
// later when an object is no longer needed:
myObj = null;
console.log(cache.size); // 1
In case of calling process(obj) with the same object multiple times, it will calculate the result only the first time. Afterward, it will take the information from the cache. (如果使用同一对象多次调用process (obj) ,则仅在第一次调用时计算结果。之后,它将从缓存中获取信息。)
The only disadvantage of caching is that you need to clean the cache once you don’t need the object anymore. (缓存的唯一缺点是,一旦不再需要对象,就需要清理缓存。)
Replacing Map with WeakMap will resolve the problem. The cached information will be deleted from memory automatically once the object has garbage collected. (用WeakMap替换Map将解决问题。对象收集垃圾后,缓存的信息将自动从内存中删除。)
To be more precise, let’s consider the example below:
// cache.js
let cache = new WeakMap();
// calculate and remember the result
function process(obj) {
if (!cache.has(obj)) {
let res = /* calculate the result for */ obj;
cache.set(obj, res);
}
return cache.get(obj);
}
// main.js
let obj = { /* some object */ };
let res1 = process(obj);
let res2 = process(obj);
// later, when object is no longer needed:
obj = null;
// Can not get cache.size because it is WeakMap,
// but it is 0 or soon will be 0
//Once object gets garbage collected, the cached data will also be cleaned
WeakSet
WeakSet
WeakSet is considered equivalent to Set. Yet, only objects and not primitives may be added to WeakSet. (WeakSet被认为等同于Set。然而,只有对象而不是原语可以添加到WeakSet。)
WeakSet also supports has, add, and delete. But no iterations or size and keys()are supported. (WeakSet还支持HAS、ADD和DELETE。但不支持迭代或大小和键()。)
Also, it can serve as additional storage for data, but not for arbitrary data. Here is an example of adding languages to WeakSet for keeping track of the ones that created the site:
let createdSet = new WeakSet();
let html = {
name: "Html"
};
let php = {
name: "php"
};
let js = {
name: "Javascript"
};
createdSet.add(html); // Html created us
createdSet.add(js); // Then Javascript
createdSet.add(html); // Html again
// createdSet has 2 languages now
// check if html created?
console.log(createdSet.has(html)); // true
// check if php created?
console.log(createdSet.has(php)); // false
html = null;
// createdSet will be cleaned automatically
There is a significant limitation in WeakMap and WeakSet: there are no iterations. Neither there is an ability to receive all the current content.
Summary
Summary (概要)
In this chapter, we covered WeakMap and WeakSet. (在本章中,我们介绍了WeakMap和WeakSet。)
To sum up, we can state that WeakMap is considered a Map-like collection, allowing merely objects to be keys. It deletes them along with associated value when they become inaccessible. (综上所述,我们可以说WeakMap被认为是一个类似Map的集合,只允许对象作为键。当它们变得不可访问时,它会删除它们以及关联的值。)
WeakSet is considered a Set-like collection, storing merely objects and deleting them when they become inaccessible. (WeakSet被认为是一个类似集合的集合,仅存储对象,并在无法访问时删除它们。)
Both collections don’t support properties and methods not referring to all the keys or the count of them. They allow merely individual operations. (这两个集合不支持不引用所有键或其计数的属性和方法。它们仅允许单独操作。)