答案 0 :(得分:81)
当键/值引用的对象被删除时,它们的行为都不同。让我们看下面的示例代码:
var map = new Map();
var weakmap = new WeakMap();
(function(){
var a = {x: 12};
var b = {y: 12};
map.set(a, 1);
weakmap.set(b, 2);
})()
执行上述IIFE我们无法再引用{x: 12}
和{y: 12}
。垃圾收集器继续执行并从“WeakMap”中删除键b指针,并从内存中删除{y: 12}
。但是在“Map”的情况下,垃圾收集器不会从“Map”中删除指针,也不会从内存中删除{x: 12}
。
总结:WeakMap允许垃圾收集器执行其任务但不允许执行Map。
参考文献:http://qnimate.com/difference-between-map-and-weakmap-in-javascript/
答案 1 :(得分:63)
对某些人来说,下一个解释可能会更清楚。
var k1 = {a: 1};
var k2 = {b: 2};
var map = new Map();
var wm = new WeakMap();
map.set(k1, 'k1');
wm.set(k2, 'k2');
k1 = null;
map.forEach(function (val, key) {
console.log(key, val); // k1 {a: 1}
});
k2 = null;
wm.get(k2); // undefined
如您所见,从内存中删除k1
键后,我们仍然可以在地图中访问它。同时,删除WeakMap的k2
键会将其从wm
中删除,也可以通过引用将其删除。
这就是为什么WeakMap没有像forEach这样的可枚举方法,因为没有WeakMap键列表,它们只是对另一个对象的引用。
答案 2 :(得分:45)
来自the very same page, section "Why Weak Map?":
经验丰富的JavaScript程序员会注意到这个API可以 用JavaScript实现两个数组(一个用于键,一个用于 由4种API方法共享的值)。这样的实现会有 两个主要的不便之处。第一个是O(n)搜索(n是 地图中的键数)。第二个是内存泄漏问题。 使用手动编写的映射,键的数组将保留对引用的引用 关键对象,防止它们被垃圾收集。在土生土长的 WeakMaps,对关键对象的引用保持“弱”,这意味着 他们不会阻止垃圾收集,以防万一没有 对该对象的其他引用。
由于引用较弱,WeakMap键不可枚举 (即没有方法给你一个密钥列表)。如果他们是, 该列表将取决于垃圾收集的状态,介绍 非确定性。
[这就是他们没有size
财产的原因]
如果你想要一个按键列表,你应该 自己保养。还有一个ECMAScript proposal 旨在介绍不会使用弱的简单集和地图 引用并且可以枚举。
- 这将是"normal" Map
s。在MDN上未提及,但在harmony proposal中,这些也有items
,keys
和values
生成器方法并实现Iterator
interface。
答案 3 :(得分:32)
另一个区别(来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap):
WeakMaps的键只是Object类型。原始数据类型为 不允许使用密钥(例如,符号不能是WeakMap密钥)。
字符串,数字或布尔值也不能用作WeakMap
键。 Map
可以使用键的原始值。
w = new WeakMap;
w.set('a', 'b'); // Uncaught TypeError: Invalid value used as weak map key
m = new Map
m.set('a', 'b'); // Works
答案 4 :(得分:3)
来自 Javascript.info
地图-如果我们在常规Map中使用对象作为键,则当Map存在时,该对象也将存在。它占用内存,可能不会被垃圾回收。
let john = { name: "John" };
let array = [ john ];
john = null; // overwrite the reference
// john is stored inside the array, so it won't be garbage-collected
// we can get it as array[0]
与此类似,如果我们在常规Map中使用对象作为键,则在Map存在时,该对象也将存在。它占用内存,可能不会被垃圾回收
let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // overwrite the reference
// john is stored inside the map,
// we can get it by using map.keys()
WeakMap –现在,如果我们使用对象作为键,并且对该对象没有其他引用,则会自动从内存(和地图)中删除该对象。
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // overwrite the reference
// john is removed from memory!
答案 5 :(得分:2)
javascript中的WeapMap不包含任何键或值,它只是使用唯一ID 操作键值并将属性定义为键对象。
因为它通过方法key
将属性定义为Object.definePropert()
,所以key不能是原始类型。
并且因为WeapMap实际上不包含键值对,所以我们无法获得weakmap的length属性。
并且操纵值也被分配回密钥,垃圾收集器很容易收集密钥,如果它没有用。
实施的示例代码。
if(typeof WeapMap != undefined){
return;
}
(function(){
var WeapMap = function(){
this.__id = '__weakmap__';
}
weakmap.set = function(key,value){
var pVal = key[this.__id];
if(pVal && pVal[0] == key){
pVal[1]=value;
}else{
Object.defineProperty(key, this.__id, {value:[key,value]});
return this;
}
}
window.WeakMap = WeakMap;
})();
的参考
答案 6 :(得分:0)
WeakMap
键必须是对象,而不是原始值。
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "ok"); // works fine (object key)
// can't use a string as the key
weakMap.set("test", "Not ok"); // Error, because "test" is not an object
为什么????
让我们看下面的示例。
let user = { name: "User" };
let map = new Map();
map.set(user, "...");
user = null; // overwrite the reference
// 'user' is stored inside the map,
// We can get it by using map.keys()
如果我们在常规
Map
中使用对象作为键,则Map
存在,该对象也存在。它占用内存并且可能 不会被垃圾收集。
WeakMap
在这方面根本不同。没有 防止对关键对象进行垃圾收集。
let user = { name: "User" };
let weakMap = new WeakMap();
weakMap.set(user, "...");
user = null; // overwrite the reference
// 'user' is removed from memory!
如果我们使用对象作为键,并且没有其他键 对该对象的引用-它将从内存中(以及从 地图)。
WeakMap
不不支持迭代和方法 keys(), values(), entries()< / em>,因此无法从中获取所有键或值。
WeakMap仅具有以下方法:
很明显,好像一个对象丢失了所有其他引用(例如上面的代码中的“ user”),则该对象将被自动垃圾收集。但是从技术上讲,清理时并没有完全指定。
JavaScript引擎决定了这一点。当发生更多删除操作时,它可以选择立即执行内存清理,或者等待并稍后进行清理。因此,从技术上讲,WeakMap
的当前元素计数是未知的。引擎可能已经清理过或没有清理过或部分清理过。因此,不支持访问所有键/值的方法。
注意:- WeakMap的应用程序主要区域是附加数据存储。就像将对象缓存到该对象被垃圾回收一样。