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

由于弱映射的键创建了对其对应值的强引用,确保插入到弱映射中的值只要其键仍然存在就不会消失,因此它不能用于备忘录表、缓存或其他通常使用弱引用、具有弱值的映射等的任何东西。

在我看来:

weakmap.set(key, value);

...只是拐弯抹角地说

key.value = value;

我遗漏了哪些具体的用例?


当前回答

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

其他回答

WEAKMAP:请记住,WEAKMAP是关于内存分配和垃圾收集的,并且只与对象类型的键相关 在javascript中,当你存储值在键值对数组,映射,设置等…一个分配给所有键值对的内存,即使你删除或将该键设置为null,这个内存也不会被释放,将此视为强映射键强附加到内存下面是一个例子

let john = { name: "yusuf" };

let map = new Map();
map.set(yusuf, "xyz"); //here "yusuf" is the key and "xyz" is value

yusuf= null; // overwrite the reference

// the object previously referenced by yusuf is stored inside the array
// therefore it won't be garbage-collected
// we can get it using map.keys()

但这不是weakMap的情况,在这里内存将是空闲的

let john = { name: "yusuf" };

let map = new WeakMap();
map.set(yusuf, "...");

yusuf= null; // overwrite the reference

// yusuf is removed from memory!

用例:你会在javascript中使用它,你想以更有效的方式管理内存

如果我们正在处理一个“属于”另一个代码的对象,甚至可能是一个第三方库,并且想要存储一些与之相关的数据,这些数据应该只在对象存活时存在——那么WeakMap正是所需要的。

我们将数据放到WeakMap中,使用对象作为键,当对象被垃圾收集时,该数据也将自动消失。

weakMap.set(yusuf, "secret documents");
// if yusuf dies, secret documents will be destroyed automatically

我参考了这篇很棒的文章:https://javascript.info/weakmap-weakset

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

我认为这是非常有用的检查连接收入在应用程序套接字。 另一种情况,'Weak Collection'是有用的:https://javascript.info/task/recipients-read

这个答案似乎是有偏见的,在现实世界中是不可用的。请原原本本地阅读,不要把它看作是一种实际的选择,而只是一种实验

用例可以是将它用作监听器的字典,我有一个同事就是这样做的。这是非常有用的,因为任何听众都可以直接以这种方式做事。再见listener.on。

但是从更抽象的角度来看,WeakMap在实现对任何东西的非实体化访问方面特别强大,您不需要一个名称空间来隔离它的成员,因为它已经被这种结构的性质所暗示。我很确定你可以通过替换尴尬的冗余对象键来做一些重大的内存改进(即使解构可以为你工作)。


在阅读接下来的内容之前

我现在意识到我所强调的并不是解决问题的最佳方法,正如Benjamin Gruenbaum所指出的(请查看他的答案,如果它还没有高于我的答案:p),这个问题不可能用常规Map解决,因为它会泄漏,因此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);
    }
}

我使用WeakMap缓存以不可变对象作为参数的函数。

记忆是一种花哨的说法,意思是“在你计算出值之后,缓存它,这样你就不必再次计算它”。

这里有一个例子:

// using immutable.js from here https://facebook.github.io/immutable-js/ const memo = new WeakMap(); let myObj = Immutable.Map({a: 5, b: 6}); function someLongComputeFunction (someImmutableObj) { // if we saved the value, then return it if (memo.has(someImmutableObj)) { console.log('used memo!'); return memo.get(someImmutableObj); } // else compute, set, and return const computedValue = someImmutableObj.get('a') + someImmutableObj.get('b'); memo.set(someImmutableObj, computedValue); console.log('computed value'); return computedValue; } someLongComputeFunction(myObj); someLongComputeFunction(myObj); someLongComputeFunction(myObj); // reassign myObj = Immutable.Map({a: 7, b: 8}); someLongComputeFunction(myObj); <script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>

有几件事需要注意:

当你修改Immutable.js对象时返回新对象(带有新指针),因此在WeakMap中使用它们作为键可以保证相同的计算值。 WeakMap非常适合memo,因为一旦对象(用作键)被垃圾收集,WeakMap上的计算值也会被垃圾收集。