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()
方法检索与对象关联的项。WeakMap
s的一个有趣的方面是,它对映射内的键具有弱引用。弱引用意味着如果对象被销毁,垃圾收集器将从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
对于基本上对任何内容的非物质化访问都特别强大,您不需要名称空间来隔离其成员,因为此结构的性质已经暗示了它。我敢肯定,通过替换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);
}
}
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);
}
内存泄漏消失了。
一些用例可能会导致内存泄漏,并由WeakMap
s启用,包括:
它可用于从外部延伸对象。让我们从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