ECMAScript 6中引入的WeakMap数据结构的实际用途是什么?
由于弱映射的键创建了对其对应值的强引用,确保插入到弱映射中的值只要其键仍然存在就不会消失,因此它不能用于备忘录表、缓存或其他通常使用弱引用、具有弱值的映射等的任何东西。
在我看来:
weakmap.set(key, value);
...只是拐弯抹角地说
key.value = value;
我遗漏了哪些具体的用例?
ECMAScript 6中引入的WeakMap数据结构的实际用途是什么?
由于弱映射的键创建了对其对应值的强引用,确保插入到弱映射中的值只要其键仍然存在就不会消失,因此它不能用于备忘录表、缓存或其他通常使用弱引用、具有弱值的映射等的任何东西。
在我看来:
weakmap.set(key, value);
...只是拐弯抹角地说
key.value = value;
我遗漏了哪些具体的用例?
当前回答
我有一个简单的基于弱地图功能的用例/示例。
管理用户集合
我从一个User对象开始,它的属性包括全名、用户名、年龄、性别和一个名为print的方法,该方法打印人类可读的其他属性的摘要。
/**
Basic User Object with common properties.
*/
function User(username, fullname, age, gender) {
this.username = username;
this.fullname = fullname;
this.age = age;
this.gender = gender;
this.print = () => console.log(`${this.fullname} is a ${age} year old ${gender}`);
}
然后,我添加了一个名为users的Map,以保存多个用户的集合,这些用户由用户名输入。
/**
Collection of Users, keyed by username.
*/
var users = new Map();
Collection的添加还需要辅助函数来添加、获取、删除User,甚至为了完整起见,还需要一个函数来打印所有用户。
/**
Creates an User Object and adds it to the users Collection.
*/
var addUser = (username, fullname, age, gender) => {
let an_user = new User(username, fullname, age, gender);
users.set(username, an_user);
}
/**
Returns an User Object associated with the given username in the Collection.
*/
var getUser = (username) => {
return users.get(username);
}
/**
Deletes an User Object associated with the given username in the Collection.
*/
var deleteUser = (username) => {
users.delete(username);
}
/**
Prints summary of all the User Objects in the Collection.
*/
var printUsers = () => {
users.forEach((user) => {
user.print();
});
}
在运行上述所有代码时,比如NodeJS,在整个进程中只有用户映射具有对用户对象的引用。没有其他对单个用户对象的引用。
在交互式NodeJS shell中运行这段代码,就像一个例子,我添加了四个用户并打印他们:
在不修改现有代码的情况下向用户添加更多信息
现在,假设需要一个新功能,其中每个用户的社交媒体平台(SMP)链接需要与用户对象一起跟踪。
这里的关键还在于,该特性的实现必须对现有代码进行最少的干预。
这可以通过以下方式使用weakmap实现。
我为Twitter, Facebook, LinkedIn添加了三个单独的弱地图。
/*
WeakMaps for Social Media Platforms (SMPs).
Could be replaced by a single Map which can grow
dynamically based on different SMP names . . . anyway...
*/
var sm_platform_twitter = new WeakMap();
var sm_platform_facebook = new WeakMap();
var sm_platform_linkedin = new WeakMap();
添加帮助函数getSMPWeakMap只是为了返回与给定SMP名称相关联的弱映射。
/**
Returns the WeakMap for the given SMP.
*/
var getSMPWeakMap = (sm_platform) => {
if(sm_platform == "Twitter") {
return sm_platform_twitter;
}
else if(sm_platform == "Facebook") {
return sm_platform_facebook;
}
else if(sm_platform == "LinkedIn") {
return sm_platform_linkedin;
}
return undefined;
}
将用户SMP链接添加到给定SMP弱映射的函数。
/**
Adds a SMP link associated with a given User. The User must be already added to the Collection.
*/
var addUserSocialMediaLink = (username, sm_platform, sm_link) => {
let user = getUser(username);
let sm_platform_weakmap = getSMPWeakMap(sm_platform);
if(user && sm_platform_weakmap) {
sm_platform_weakmap.set(user, sm_link);
}
}
只打印在给定SMP上的用户的函数。
/**
Prints the User's fullname and corresponding SMP link of only those Users which are on the given SMP.
*/
var printSMPUsers = (sm_platform) => {
let sm_platform_weakmap = getSMPWeakMap(sm_platform);
console.log(`Users of ${sm_platform}:`)
users.forEach((user)=>{
if(sm_platform_weakmap.has(user)) {
console.log(`\t${user.fullname} : ${sm_platform_weakmap.get(user)}`)
}
});
}
您现在可以为用户添加SMP链接,还可以让每个用户在多个SMP上拥有一个链接。
…继续前面的示例,我为用户添加SMP链接,为用户Bill和Sarah添加多个链接,然后分别打印每个SMP的链接:
现在,假设通过调用deleteUser从用户映射中删除了一个User。这将删除对User对象的唯一引用。这反过来也将从任何/所有SMP弱映射中清除SMP链接(通过垃圾收集),因为没有User对象就没有办法访问它的任何SMP链接。
...继续示例,我删除用户Bill,然后打印出与他相关的smp的链接:
不需要任何额外的代码来单独删除SMP链接,并且在此特性之前的现有代码没有任何修改。
如果有任何其他方式添加这个功能,或没有弱地图,请随时评论。
其他回答
我认为这是非常有用的检查连接收入在应用程序套接字。 另一种情况,'Weak Collection'是有用的:https://javascript.info/task/recipients-read
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
我有一个简单的基于弱地图功能的用例/示例。
管理用户集合
我从一个User对象开始,它的属性包括全名、用户名、年龄、性别和一个名为print的方法,该方法打印人类可读的其他属性的摘要。
/**
Basic User Object with common properties.
*/
function User(username, fullname, age, gender) {
this.username = username;
this.fullname = fullname;
this.age = age;
this.gender = gender;
this.print = () => console.log(`${this.fullname} is a ${age} year old ${gender}`);
}
然后,我添加了一个名为users的Map,以保存多个用户的集合,这些用户由用户名输入。
/**
Collection of Users, keyed by username.
*/
var users = new Map();
Collection的添加还需要辅助函数来添加、获取、删除User,甚至为了完整起见,还需要一个函数来打印所有用户。
/**
Creates an User Object and adds it to the users Collection.
*/
var addUser = (username, fullname, age, gender) => {
let an_user = new User(username, fullname, age, gender);
users.set(username, an_user);
}
/**
Returns an User Object associated with the given username in the Collection.
*/
var getUser = (username) => {
return users.get(username);
}
/**
Deletes an User Object associated with the given username in the Collection.
*/
var deleteUser = (username) => {
users.delete(username);
}
/**
Prints summary of all the User Objects in the Collection.
*/
var printUsers = () => {
users.forEach((user) => {
user.print();
});
}
在运行上述所有代码时,比如NodeJS,在整个进程中只有用户映射具有对用户对象的引用。没有其他对单个用户对象的引用。
在交互式NodeJS shell中运行这段代码,就像一个例子,我添加了四个用户并打印他们:
在不修改现有代码的情况下向用户添加更多信息
现在,假设需要一个新功能,其中每个用户的社交媒体平台(SMP)链接需要与用户对象一起跟踪。
这里的关键还在于,该特性的实现必须对现有代码进行最少的干预。
这可以通过以下方式使用weakmap实现。
我为Twitter, Facebook, LinkedIn添加了三个单独的弱地图。
/*
WeakMaps for Social Media Platforms (SMPs).
Could be replaced by a single Map which can grow
dynamically based on different SMP names . . . anyway...
*/
var sm_platform_twitter = new WeakMap();
var sm_platform_facebook = new WeakMap();
var sm_platform_linkedin = new WeakMap();
添加帮助函数getSMPWeakMap只是为了返回与给定SMP名称相关联的弱映射。
/**
Returns the WeakMap for the given SMP.
*/
var getSMPWeakMap = (sm_platform) => {
if(sm_platform == "Twitter") {
return sm_platform_twitter;
}
else if(sm_platform == "Facebook") {
return sm_platform_facebook;
}
else if(sm_platform == "LinkedIn") {
return sm_platform_linkedin;
}
return undefined;
}
将用户SMP链接添加到给定SMP弱映射的函数。
/**
Adds a SMP link associated with a given User. The User must be already added to the Collection.
*/
var addUserSocialMediaLink = (username, sm_platform, sm_link) => {
let user = getUser(username);
let sm_platform_weakmap = getSMPWeakMap(sm_platform);
if(user && sm_platform_weakmap) {
sm_platform_weakmap.set(user, sm_link);
}
}
只打印在给定SMP上的用户的函数。
/**
Prints the User's fullname and corresponding SMP link of only those Users which are on the given SMP.
*/
var printSMPUsers = (sm_platform) => {
let sm_platform_weakmap = getSMPWeakMap(sm_platform);
console.log(`Users of ${sm_platform}:`)
users.forEach((user)=>{
if(sm_platform_weakmap.has(user)) {
console.log(`\t${user.fullname} : ${sm_platform_weakmap.get(user)}`)
}
});
}
您现在可以为用户添加SMP链接,还可以让每个用户在多个SMP上拥有一个链接。
…继续前面的示例,我为用户添加SMP链接,为用户Bill和Sarah添加多个链接,然后分别打印每个SMP的链接:
现在,假设通过调用deleteUser从用户映射中删除了一个User。这将删除对User对象的唯一引用。这反过来也将从任何/所有SMP弱映射中清除SMP链接(通过垃圾收集),因为没有User对象就没有办法访问它的任何SMP链接。
...继续示例,我删除用户Bill,然后打印出与他相关的smp的链接:
不需要任何额外的代码来单独删除SMP链接,并且在此特性之前的现有代码没有任何修改。
如果有任何其他方式添加这个功能,或没有弱地图,请随时评论。
我使用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上的计算值也会被垃圾收集。
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;
}());
})()