查看这个和这个MDN 页面,似乎 Maps 和 WeakMaps 之间的唯一区别是 WeakMaps 缺少“大小”属性。但这是真的吗?他们之间有什么区别?
7 回答
当它们的键/值引用的对象被删除时,它们的行为都不同。让我们看下面的示例代码:
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/
也许下一个解释对某人来说会更清楚。
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
从内存中删除密钥后,我们仍然可以在地图中访问它。同时删除k2
WeakMap 的键wm
也会通过引用将其删除。
这就是为什么 WeakMap 没有像 forEach 这样的可枚举方法,因为没有 WeakMap 键列表之类的东西,它们只是对另一个对象的引用。
有经验的 JavaScript 程序员会注意到,这个 API 可以用 JavaScript 实现,两个数组(一个用于键,一个用于值)由 4 个 API 方法共享。这样的实现将有两个主要的不便之处。第一个是 O(n) 搜索(n 是地图中的键数)。第二个是内存泄漏问题。使用手动编写的映射,键数组将保留对键对象的引用,防止它们被垃圾收集。在本机 WeakMaps 中,对关键对象的引用是“弱”的,这意味着它们不会阻止垃圾收集,以防没有其他对该对象的引用。
由于引用很弱,WeakMap 键是不可枚举的(即没有方法可以为您提供键列表)。如果是,则该列表将取决于垃圾收集的状态,从而引入非确定性。
[这就是为什么他们也没有size
财产]
如果你想要一个键列表,你应该自己维护它。还有一个ECMAScript 提案 旨在引入不使用弱引用且可枚举的简单集合和映射。
- 这将是“正常”Map
的 s。在 MDN 中没有提到,但在和声提案中,那些也有items
,keys
和values
生成器方法并实现Iterator
接口。
另一个区别(来源:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):
WeakMaps 的键仅属于 Object 类型。不允许将原始数据类型作为键(例如,Symbol 不能是 WeakMap 键)。
字符串、数字或布尔值也不能用作WeakMap
键。AMap
可以使用原始值作为键。
w = new WeakMap;
w.set('a', 'b'); // Uncaught TypeError: Invalid value used as weak map key
m = new Map
m.set('a', 'b'); // Works
来自Javascript.info
Map -- 如果我们在常规 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!
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(),因此无法从中获取所有键或值。
WeakMap 只有以下方法:
- weakMap.get(key)
- weakMap.set(键,值)
- 弱映射.删除(键)
- weakMap.has(key)
这很明显,好像一个对象已经丢失了所有其他引用(如上面代码中的“用户”),那么它将自动被垃圾收集。但从技术上讲,清理发生的时间并没有准确说明。
JavaScript 引擎决定了这一点。它可以选择立即执行内存清理,或者在发生更多删除时等待并稍后执行清理。因此,从技术上讲,a 的当前元素计数WeakMap
是未知的。引擎可能已经清理或没有清理或部分清理。因此,不支持访问所有键/值的方法。
注意:- WeakMap 的主要应用领域是附加数据存储。就像缓存一个对象,直到该对象被垃圾收集。
javascript 中的 WeapMap 不保存任何键或值,它只是使用唯一 id操作键值并为键对象定义一个属性。
因为它是key object
通过方法定义属性的Object.definePropert()
,所以key不能是原始类型。
也因为 WeapMap 不包含实际的键值对,我们无法获取弱映射的长度属性。
并且还将操作值分配回密钥对象,如果密钥不使用,垃圾收集器可以轻松收集密钥。
实现示例代码。
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;
})();
实施参考