【问题标题】:What's the difference between ES6 Map and WeakMap?ES6 Map 和 WeakMap 有什么区别?
【发布时间】:2013-03-14 07:21:05
【问题描述】:

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

【问题讨论】:

  • 效果在GC上。 WeakMap 可以收集它们的密钥。
  • @JanDvorak MDN 上没有关于它的示例。像aWeakMap.get(key); // 比如说 2 ...(GC 动作)... aWeakMap.get(key); // 比如说,未定义
  • 你的例子是不可能的。 key 不能被收集,因为它被你引用了。
  • 设计决策是 GC 动作在 Javascript 中是不可见的。你不能观察 GC 做它的事情。
  • 有关此问题的更多信息,请参阅this related answer

标签: javascript ecmascript-6 ecmascript-harmony weakmap


【解决方案1】:

来自the very same page, section "Why Weak Map?"

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

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

[这就是为什么他们也没有size 属性]

如果你想要一个键列表,你应该 自己维护。还有一个ECMAScript proposal 旨在引入不会使用弱的简单集合和地图 引用并且是可枚举的。

- 这将是"normal" Maps。在 MDN 中没有提到,但在 harmony proposal 中,它们也有 itemskeysvalues 生成器方法并实现了 Iterator interface

【讨论】:

  • 所以new Map().get(x) 的查找时间与从普通对象中读取属性的时间差不多?
  • @AlexanderMills 我不明白这与问题有什么关系,但here is some data。一般来说,yes they are similaryou should use the appropriate one
  • 所以我的理解是 Map 维护一个内部数组来保存它的密钥,因为该数组。垃圾收集器无法抑制引用。在 WeekMap 中,它没有一个数组来维护键,因此可以对没有引用的键进行垃圾收集。
  • @MohanRam A WeakMap 仍然有一个条目数组(或其他集合),它只是告诉垃圾收集器这些是weak references
  • 那为什么不支持 WeekMap 键的迭代?
【解决方案2】:

另一个区别(来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap):

WeakMaps 的键仅属于 Object 类型。原始数据类型为 不允许使用键(例如,Symbol 不能是 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

【讨论】:

  • 如果有人想知道:我可以想象这背后的原因是:您不能保留或传递对原始类型的引用。因此,WeakMap 中的键永远是它的唯一参考。这样垃圾收集就不可能了。我不知道弱引用是不可能的还是没有意义。但无论哪种方式,关键都需要是可以被弱引用的东西。
【解决方案3】:

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

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/

【讨论】:

  • 为什么没有从内存中删除?因为你仍然可以参考它! map.entries().next().value // [{x:12}, 1]
  • 它不是一个自调用函数。这是一个立即调用的函数表达式。 benalman.com/news/2010/11/…
  • 那么weakmap和object有什么区别
  • @MuhammadUmer: 对象只能有字符串“键”,而WeakMap 只能有非原始键(没有字符串或数字或Symbols 作为键,只有数组、对象、其他地图等)。
  • @nnnnnn 是的,这就是区别,它仍在Map 中,但不在WeakMap
【解决方案4】:

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

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 键列表之类的东西,它们只是对另一个对象的引用。

【讨论】:

  • 在最后一行,当然wm.get(null)是未定义的。
  • 比从 mozilla 网站复制和粘贴更好的答案,赞。
  • forEach中,(key, val)实际上应该是(val, key)
  • 令人难以置信的是,一个毫无意义的示例如何获得如此多的支持
【解决方案5】:

JavaScript 中的 WeapMap 不保存任何键或值,它只是使用 唯一 id 操作键值并为键对象定义一个属性。

因为它通过Object.definePropert()方法给key object定义了属性,所以key不能是原始类型

而且由于 WeapMap 不包含实际的键值对,我们无法获取 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;
})();

implementation的引用

【讨论】:

  • 明确地说,这个实现只有一半有效。它不允许在多个弱映射中使用相同的对象作为键。它也不适用于冻结的对象。当然,它会将映射泄露给任何引用该对象的人。第一个可以用符号来固定,但不能用后两个来固定。
  • @AndreasRossberg 在这个实现中,我添加了硬编码的id,但这应该是唯一的,通过使用 Math.random 和 Date.now() 等。通过添加这个动态 id,第一个点可以解决。您能否为我提供最后两点的解决方案。
  • 第一个问题通过使用符号更优雅地解决了。后两者在 JS 中无法解决,这就是为什么 WeakMap 必须是语言中的原语。
【解决方案6】:

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(key, value)
  • weakMap.delete(key)
  • weakMap.has(key)

这很明显,好像一个对象已经丢失了所有其他引用(如上面代码中的“用户”),然后它会自动被垃圾收集。但从技术上讲,并没有准确说明清理发生的时间。

JavaScript 引擎决定了这一点。它可以选择立即执行内存清理,或者在发生更多删除时等待并稍后执行清理。因此,从技术上讲,WeakMap 的当前元素计数是未知的。引擎可能已经清理或没有清理或部分清理。因此,不支持访问所有键/值的方法。

注意:- WeakMap 的主要应用领域是额外的数据存储。就像缓存一个对象,直到该对象被垃圾收集。

【讨论】:

    【解决方案7】:

    来自 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!
    

    【讨论】:

      猜你喜欢
      • 2018-03-08
      • 2015-12-19
      • 2017-03-21
      • 2016-12-24
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-16
      相关资源
      最近更新 更多