当前位置: 首页 > 知识库问答 >
问题:

ES6 WeakMap的实际用途是什么?

益何平
2023-03-14

ECMAScript 6中引入的WeakMap数据结构的实际用途是什么?

由于弱映射的键创建对其对应值的强引用,确保插入到弱映射中的值在其键仍处于活动状态时永远不会消失,因此它不能用于备注表、缓存或您通常使用弱引用、具有弱值的映射等的任何其他内容。

在我看来:

weakmap.set(key, value);

…只是一种迂回的说法:

key.value = value;

我缺少哪些具体的用例?

共有3个答案

万高畅
2023-03-14

WeakMap适用于封装和信息隐藏

WeakMap仅适用于ES6及以上版本。WeakMap是键和值对的集合,其中键必须是对象。在下面的示例中,我们构建了一个包含两项的WeakMap

var map = new WeakMap();
var pavloHero = {first: "Pavlo", last: "Hero"};
var gabrielFranco = {first: "Gabriel", last: "Franco"};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero));//This is Hero

我们使用set()。我们使用get()方法检索与对象关联的项。WeakMaps的一个有趣的方面是,它对映射内的键具有弱引用。弱引用意味着如果对象被销毁,垃圾收集器将从WeakMap中删除整个条目,从而释放内存。

var TheatreSeats = (function() {
  var priv = new WeakMap();
  var _ = function(instance) {
    return priv.get(instance);
  };

  return (function() {
      function TheatreSeatsConstructor() {
        var privateMembers = {
          seats: []
        };
        priv.set(this, privateMembers);
        this.maxSize = 10;
      }
      TheatreSeatsConstructor.prototype.placePerson = function(person) {
        _(this).seats.push(person);
      };
      TheatreSeatsConstructor.prototype.countOccupiedSeats = function() {
        return _(this).seats.length;
      };
      TheatreSeatsConstructor.prototype.isSoldOut = function() {
        return _(this).seats.length >= this.maxSize;
      };
      TheatreSeatsConstructor.prototype.countFreeSeats = function() {
        return this.maxSize - _(this).seats.length;
      };
      return TheatreSeatsConstructor;
    }());
})()

翁鸿远
2023-03-14

一个用例可以是将它用作听众的字典,我有一个同事这样做了。这是非常有帮助的,因为任何听者都直接以这种方式做事。再见收听者。

但从更抽象的角度来看,WeakMap对于基本上对任何内容的非物质化访问都特别强大,您不需要名称空间来隔离其成员,因为此结构的性质已经暗示了它。我敢肯定,通过替换awkwards冗余对象键,您可以实现一些重大的内存改进(即使解构可以为您完成这项工作)。

我现在确实意识到我的强调并不是解决问题的最佳方式,正如本杰明·格伦鲍姆(Benjamin Gruenbaum)指出的(查看他的答案,如果它不在我的答案之上:p),这个问题不可能用常规的地图解决,因为它会泄漏,因此,WeakMap的主要优点是,它不会干扰垃圾收集,因为它们不保留引用。

这是我同事的实际代码(感谢他分享)

这里有完整的源代码,是关于我上面提到的侦听器管理的(您也可以查看规范)

var listenableMap = new WeakMap();


export function getListenable (object) {
    if (!listenableMap.has(object)) {
        listenableMap.set(object, {});
    }

    return listenableMap.get(object);
}


export function getListeners (object, identifier) {
    var listenable = getListenable(object);
    listenable[identifier] = listenable[identifier] || [];

    return listenable[identifier];
}


export function on (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    listeners.push(listener);
}


export function removeListener (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    var index = listeners.indexOf(listener);
    if(index !== -1) {
        listeners.splice(index, 1);
    }
}


export function emit (object, identifier, ...args) {
    var listeners = getListeners(object, identifier);

    for (var listener of listeners) {
        listener.apply(object, args);
    }
}

谷梁迪
2023-03-14

WeakMaps提供了一种从外部扩展对象而不干扰垃圾回收机制的方法。每当您想扩展一个对象,但由于它是密封的或来自外部源而无法扩展时,可以应用WeakMap。

WeakMap是一个映射(字典),其中键是弱的——也就是说,如果对键的所有引用都丢失了,并且没有对值的更多引用——值可以被垃圾收集。让我们先通过例子来展示这一点,然后稍微解释一下,最后以实际使用来结束。

假设我使用的API为我提供了一个特定的对象:

var obj = getObjectFromLibrary();

现在,我有一个使用对象的方法:

function useObj(obj){
   doSomethingWith(obj);
}

我想跟踪有多少次的方法被调用的某个对象,并报告如果它发生超过N次。天真的人会认为使用地图:

var map = new Map(); // maps can have object keys
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

这是可行的,但它存在内存泄漏-我们现在跟踪传递给函数的每个库对象,以防止库对象被垃圾收集。相反,我们可以使用WeakMap

var map = new WeakMap(); // create a weak map
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

内存泄漏消失了。

一些用例可能会导致内存泄漏,并由WeakMaps启用,包括:

  • 保留关于特定对象的私人数据,并且只允许引用地图的人访问它。私人符号提案带来了一种更特别的方法,但那是很久以后的事了。
  • 保持库对象的数据,而不改变它们或产生开销。
  • 保留关于一小群对象的数据,其中存在许多类型的对象,以避免JS引擎为相同类型的对象使用的隐藏类出现问题。
  • 在浏览器中保存有关主机对象(如DOM节点)的数据。
  • 从外部向对象添加能力(如另一个答案中的事件发射器示例)。

它可用于从外部延伸对象。让我们从Node.html" target="_blank">js的真实世界中给出一个实际的(经过调整的,有点真实的,以说明问题)示例。

假设您是Node.js,您有Promise对象—现在您希望跟踪所有当前被拒绝的promise—但是,如果不存在对它们的引用,您不希望阻止它们被垃圾收集。

现在,由于明显的原因,您不想将属性添加到本机对象,所以您被卡住了。如果您保持对promise的引用,则会导致内存泄漏,因为不会发生垃圾收集。如果你不保留推荐信,那么你就不能保存关于个人promise的额外信息。任何涉及保存promiseID的方案本质上意味着您需要对其进行引用。

弱地图意味着键是弱的。没有办法枚举弱映射或获取其所有值。在弱映射中,您可以基于键存储数据,当键被垃圾收集时,值也会被垃圾收集。

这意味着,给定一个promise,您可以存储关于它的状态,并且该对象仍然可以被垃圾收集。稍后,如果您得到一个对象的引用,您可以检查是否有任何与之相关的状态并报告它。

Petka Antonov将其用于实现未处理的拒绝挂钩,如下所示:

process.on('unhandledRejection', function(reason, p) {
    console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
    // application specific logging, throwing an error, or other logic here
});

我们将有关promise的信息保存在地图中,并且可以知道何时处理了拒绝的promise。

 类似资料:
  • 问题内容: ECMAScript 6中引入的数据结构的实际用途是什么? 由于弱映射的键会对其对应的值建立强引用,因此确保只要弱键仍处于活动状态,插入到弱映射中的值就 永远不会 消失,因此不能将其用于备忘录表,缓存或通常使用弱引用,具有弱值的映射等的其他任何内容。 在我看来,这是: …只是一种回旋的说法: 我缺少哪些具体用例? 问题答案: 从根本上 WeakMaps提供了一种从外部扩展对象而不干扰垃

  • 问题内容: 我正在尽最大的努力来解决JavaScript闭包问题。 通过返回一个内部函数,我可以访问其直接父级中定义的任何变量。 这对我有用吗?也许我还没有完全解决这个问题。我在网上看到的大多数示例都没有提供任何真实的代码,只是模糊的示例。 有人可以告诉我现实世界中使用闭包吗? 例如这个吗? 问题答案: 我用闭包来做类似的事情: 如您所见,现在有一个对象,带有一个方法()调用,该方法仅存在于闭包内

  • 当function.identity()返回与它接收到的相同的东西时,我为什么要使用它,而不使用输入或以某种方式修改输入? 这一定有一些实际的用法,但我不知道。

  • 问题内容: 在Java中,接口的任何使用都由抽象类实现。我知道接口的一个优点是,如果我们实现一个接口,那么我们也可以扩展另一个类。Java接口还有其他用途或优点吗? 问题答案: 您喜欢什么:在一个Abstract类中成千上万个抽象方法并继承该类,或者为特定的抽象方法创建尽可能多的接口,并通过继承所需的任意数量的接口来仅使用您想要的那些方法… 因此,仅通过继承特定接口使用该方法即可,如果您要继承类,

  • 问题内容: 我知道Javassist是一个Java库,提供了一种操作应用程序的Java字节码的方法。 好的,但是为什么我们需要操纵字节码? 有什么真实的例子吗?任何真正的应用程序,javassist在哪里使用? 问题答案: 一个常见的应用是在运行时生成代理类,即在运行时创建一个子类,以拦截所有方法调用。例子: Hibernate 使用代理来拦截对实体的方法调用以实现延迟加载,即,在首次访问对象时从

  • 问题内容: 我有点理解AtomicInteger和其他Atomic变量允许并发访问。但是在什么情况下通常使用此类? 问题答案: 有两个主要用途: 作为可以同时被多个线程使用的原子计数器(,等) 作为支持比较和交换指令()来实现非阻塞算法的原语。 这是BrianGöetz的中的非阻塞随机数生成器的示例: 如你所见,它的工作原理与几乎相同incrementAndGet(),但是执行任意计算(calcu