回到课程
本资料仅提供以下语言版本:English, Русский。请 帮助我们 将其翻译为 简体中文 版本。

存储 "unread" 标识

重要程度: 5

这里有一个 messages 数组:

let messages = [
    {text: "Hello", from: "John"},
    {text: "How goes?", from: "John"},
    {text: "See you soon", from: "Alice"}
];

你的代码可以访问它,但是消息被其他代码管理。这段代码有规律的添加新消息,删除旧消息,而且你不知道这些操作发生的时间。

现在,你应该是用什么数据结构来保存消息是否已读这个信息?这个结构必须很适合给出当前已知的消息对象是否已读的答案。

附:当消息被从 messages 中移除的时候,它应该也从你的数据结构中消失。

附:我们不能直接修改消息对象。如果它们被其他代码管理,那么给他们添加额外的属性可能导致不好的后果。

明智的选择是 WeakSet

let messages = [
    {text: "Hello", from: "John"},
    {text: "How goes?", from: "John"},
    {text: "See you soon", from: "Alice"}
];

let readMessages = new WeakSet();

// 两个消息已读
readMessages.add(messages[0]);
readMessages.add(messages[1]);
// readMessages 包含两个元素

// ...让我们再读一遍第一条消息!
readMessages.add(messages[0]);
// readMessages 仍然有两个不重复的元素

// 回答:message[0] 已读?
alert("Read message 0: " + readMessages.has(messages[0])); // true

messages.shift();
// 现在 readMessages 有一个元素(技术上来说内存可能稍后被清理)

WeakSet 允许存储一系列的消息并且很容易就能检查它包含的消息是否还存在。

它会自动清理自身。但是作为交换,我们不能对它进行迭代。我们不能直接获取所有已读消息。但是我们可以通过迭代所有消息然后找出存在于 set 的那些消息来完成这个功能。

附:如果消息被其他代码管理,那么仅为了自己的功能给每个消息添加一个属性也许会很危险,但是我们可以将它改为 symbol 来规避冲突。

像这样:

// the symbolic property is only known to our code
let isRead = Symbol("isRead");
messages[0][isRead] = true;

现在即使其他人的代码使用 for..in 循环消息的属性,我们的秘密标识也不会出现。