# 应用-新的数据结构

# Map

JavaScript 的对象本质上是键值对的集合,即 hash 结构,但是传统上的对象只能使用字符串作为键,这带来了很大的限制,为了解决这个问题,ES6 提供了 Map 数据结构,类似对象,但是“键”的范围不限于字符串;

  • 基本操作
let map = new Map();

map.set("1", "str1"); // 字符串键
map.set(1, "num1"); // 数字键
map.set(true, "bool1"); // 布尔值键

// 还记得普通的 Object 吗? 它会将键转化为字符串
// Map 则会保留键的类型,所以下面这两个结果不同:
alert(map.get(1)); // 'num1'
alert(map.get("1")); // 'str1'

alert(map.size); // 3
  • 遍历

如果要在 map 里使用循环,可以使用以下三个方法:

  • map.keys() —— 遍历并返回所有的键(returns an iterable for keys),
  • map.values() —— 遍历并返回所有的值(returns an iterable for values),
  • map.entries() —— 遍历并返回所有的实体(returns an iterable for entries)[key, value]
let recipeMap = new Map([
  ["cucumber", 500],
  ["tomatoes", 350],
  ["onion", 50]
]);

// 遍历所有的键(vegetables)
for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomatoes, onion
}

Map 遍历是有序的

迭代的顺序与插入值的顺序相同。与普通的 Object 不同,Map 保留了此顺序。

Map —— 是一个带键的数据项的集合。

与普通对象 Object 的不同点:

  • 任何键、对象都可以作为键。
  • 有其他的便捷方法,如 size 属性。

# Set

Set 是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。

Set 本身是一个构造函数,用来生成 Set 数据结构,可以接收一个数组作为参数用来初始化。

  • 基本操作
const set = new Set([1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9]);
console.log([...set]);

Set 的替代方法可以是一个用户数组,用 arr.find 在每次插入值时检查是否重复。但是这样性能会很差,因为这个方法会遍历整个数组来检查每个元素。Set 内部对唯一性检查进行了更好的优化。

// 数组去重
[...new Set(array)]
// 字符串去重
[...new Set(string)].join('')

向 Set 加入值的时候,不会发生类型转换,类似与全等运算符,但是判断 NaN 的时候会认为等于自身,而全等运算符不会,此外两个对象总是不相等的;

const set = new Set();
set.add(1);
set.add("1");
set.add(NaN);
set.add(NaN);
console.log([...set]);
// [ 1, '1', NaN ]
  • 遍历

Array.form() 方法可以将 Set 结构转为数组

const items = new Set([1, 2, 3, 3, 4, 5]);
const array = Array.form(items);

我们可以使用 for..offorEach 来遍历 Set:

let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) {
  console.log(value);
}

Set —— 是一组唯一值的集合。

  • 在 Map 和 Set 中迭代总是按照值插入的顺序进行的,所以我们不能说这些集合是无序的,但是我们不能对元素进行重新排序,也不能直接按其编号来获取元素。

# WeakMap

通常,当对象、数组之类的数据结构在内存中时,它们的子元素,如对象的属性、数组的元素都被认为是可达的。

例如,如果把一个对象放入到数组中,那么只要这个数组存在,那么这个对象也就存在,即使没有其他对该对象的引用。

WeakMap 和 Map 的第一个不同点就是,WeakMap 的键必须是对象,不能是原始值

let weakMap = new WeakMap();
let obj = {}
weakMap.set(obj,"ok")
weakMap.set("test","oops");// TypeError Invalid value used as weak map key

现在,如果我们在 weakMap 中使用一个对象作为键,并且没有其他对这个对象的引用 —— 该对象将会被从内存(和map)中自动清除

let john = { name: "John" };

let weakMap = new WeakMap();
weakMap.set(john, "...");

john = null; // 覆盖引用

// john 被从内存中删除了!

WeakMap 不支持迭代以及 keys(),values() 和 entries() 方法。

WeakMap 只有以下的方法:

  • weakMap.get(key)
  • weakMap.set(key, value)
  • weakMap.delete(key)
  • weakMap.has(key)

# WeakSet

WeakSet 的表现类似:

  • 与 Set 类似,但是我们只能向 WeakSet 添加对象(而不能是原始值)。
  • 对象只有在其它某个(些)地方能被访问的时候,才能留在 WeakSet 中。
  • 跟 Set 一样,WeakSet 支持 add,has 和 delete 方法,但不支持 size 和 keys(),并且不可迭代
let visitedSet = new WeakSet();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

visitedSet.add(john); // John 访问了我们
visitedSet.add(pete); // 然后是 Pete
visitedSet.add(john); // John 再次访问

// visitedSet 现在有两个用户了

// 检查 John 是否来访过?
alert(visitedSet.has(john)); // true

// 检查 Mary 是否来访过?
alert(visitedSet.has(mary)); // false