103

查看这个这个MDN 页面,似乎 Maps 和 WeakMaps 之间的唯一区别是 WeakMaps 缺少“大小”属性。但这是真的吗?他们之间有什么区别?

4

7 回答 7

108

当它们的键/值引用的对象被删除时,它们的行为都不同。让我们看下面的示例代码:

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/

于 2015-05-28T05:17:39.957 回答
84

也许下一个解释对某人来说会更清楚。

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从内存中删除密钥后,我们仍然可以在地图中访问它。同时删除k2WeakMap 的键wm也会通过引用将其删除。

这就是为什么 WeakMap 没有像 forEach 这样的可枚举方法,因为没有 WeakMap 键列表之类的东西,它们只是对另一个对象的引用。

于 2016-01-23T19:53:42.660 回答
56

同一页面上,“为什么使用地图?部分:

有经验的 JavaScript 程序员会注意到,这个 API 可以用 JavaScript 实现,两个数组(一个用于键,一个用于值)由 4 个 API 方法共享。这样的实现将有两个主要的不便之处。第一个是 O(n) 搜索(n 是地图中的键数)。第二个是内存泄漏问题。使用手动编写的映射,键数组将保留对键对象的引用,防止它们被垃圾收集。在本机 WeakMaps 中,对关键对象的引用是“弱”的,这意味着它们不会阻止垃圾收集,以防没有其他对该对象的引用。

由于引用很弱,WeakMap 键是不可枚举的(即没有方法可以为您提供键列表)。如果是,则该列表将取决于垃圾收集的状态,从而引入非确定性。

[这就是为什么他们也没有size财产]

如果你想要一个键列表,你应该自己维护它。还有一个ECMAScript 提案 旨在引入不使用弱引用且可枚举的简单集合和映射。

- 这将是“正常”Map的 s。在 MDN 中没有提到,但在和声提案中,那些也有itemskeysvalues生成器方法并实现Iterator接口

于 2013-03-24T21:35:44.613 回答
36

另一个区别(来源: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
于 2015-05-13T20:17:29.967 回答
9

来自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!
于 2020-06-02T06:38:43.527 回答
4

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 的主要应用领域是附加数据存储。就像缓存一个对象,直到该对象被垃圾收集。

于 2019-08-22T06:52:06.600 回答
3

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;
})();

实施参考

于 2018-05-27T17:41:41.143 回答