ES6系列--10. set和map

9.set和map

set

ES6 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。html

Set 与数组之间的最大区别是:es6

  • Set 不基于索引,不能根据集合中的条目在集合中的位置引用这些条目
  • Set 中的条目不能单独被访问

建立set

Set 自己是一个构造函数,用来生成 Set 数据结构。web

const games = new Set();
console.log(games);  //Set{}
复制代码

此代码会建立空的 Set games,其中没有条目。数组

Set 函数也能够接受一个数组(或者具备 iterable接口的其余数据结构)做为参数,用来初始化。bash

const set = new Set([1,2,3,3,4])
[...set]  //[1, 2, 3, 4]
set.size  //4

for (let i of s) {
  console.log(i);
}  //1, 2, 3, 4

const set = new Set('hello')
[...set] // ["h", "e", "l", "o"]

复制代码

修改 Set

建立 Set 后,你可能想要添加或删除条目。如何操做呢?可使用名称对应的 .add() 和 .delete() 方法:数据结构

const set = new Set([1,2,3,3,4])
set.add(5)
set.add('hello')  
set.delete(1)
// Set{1, 2, 3, 4, 5,'hello'}
复制代码

另外一方面,若是你想要删除 Set 中的全部条目,可使用 .clear() 方法。app

set()
console.log(set);  //Set{}
复制代码

提示:ide

  • 若是你尝试向 Set.add() 重复的条目,系统不会报错,可是该条目不会添加到 Set 中。此外,若是你尝试 .delete() Set 中不存在的条目,也不会报错,Set 保持不变。函数

  • .add() 添加无论成功与否,都会返回该 Set 对象。另外一方面,.delete() 则会返回一个布尔值,该值取决因而否成功删除(即若是该元素存在,返回true,不然返回false)。ui

使用 Set

查看长度

使用 .size 属性能够返回 Set 中的条目数:

const months = new Set(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']);
console.log(months.size);  //12
复制代码

注意,不能像数组那样经过索引访问 Set,所以要使用 .size 属性,而不是 .length 属性来获取 Set 的大小。

检查是否存在某个条目

使用 .has() 方法能够检查 Set 中是否存在某个条目。若是 Set 中有该条目,则 .has() 将返回 true。若是 Set 中不存在该条目,则 .has() 将返回 false

months.has('September')  //true
复制代码

检索全部值

Set 结构的实例有四个遍历方法,能够用于遍历成员。

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每一个成员

keys方法、values方法、entries方法返回的都是遍历器对象,因为 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),因此keys方法和values方法的行为彻底一致。

let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
复制代码

上面代码中,entries方法返回的遍历器,同时包括键名和键值,因此每次输出一个数组,它的两个成员彻底相等。

Set 结构的实例与数组同样,也拥有forEach方法,用于对每一个成员执行某种操做,没有返回值。

set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
复制代码

WeakSet

WeakSet 和普通 Set 很像,可是具备如下关键区别:

  • WeakSet 只能包含对象
  • WeakSet 没法迭代,意味着不能循环访问其中的对象
  • WeakSet 没有 .clear() 方法
const student1 = { name: 'James', age: 26, gender: 'male' };
const student2 = { name: 'Julia', age: 27, gender: 'female' };
const student3 = { name: 'Richard', age: 31, gender: 'male' };

const roster = new WeakSet([student1, student2, student3]);
console.log(roster);
//WeakSet {Object {name: 'Julia', age: 27, gender: 'female'}, Object {name: 'Richard', age: 31, gender: 'male'}, Object {name: 'James', age: 26, gender: 'male'}}
复制代码

可是若是你尝试添加对象之外的内容,系统将报错!

roster.add('Amanda'); //Uncaught TypeError: Invalid value used in weak set(…)
复制代码

其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,若是其余对象都再也不引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

这是由于垃圾回收机制依赖引用计数,若是一个值的引用次数不为0,垃圾回收机制就不会释放这块内存。结束使用该值以后,有时会忘记取消引用,致使内存没法释放,进而可能会引起内存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,因此就不存在这个问题。所以,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。

因为上面这个特色,WeakSet 的成员是不适合引用的,由于它会随时消失。另外,因为 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行先后极可能成员个数是不同的,而垃圾回收机制什么时候运行是不可预测的,所以 ES6 规定 WeakSet 不可遍历。

这些特色一样适用于后面要介绍的 WeakMap 结构。

WeakSet 结构有如下三个方法。

  • WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。

Map

若是说 Set 相似于数组,那么 Map 就相似于对象,由于 Map 存储键值对,和对象包含命名属性及值相相似。

本质上,Map是一个能够存储键值对的对象,它相似于对象,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键。

建立 Map

要建立 Map,只需输入

const employees = new Map();
console.log(employees); //Map {}
复制代码

这样就会建立空的 Map employee,没有键值对。

修改 Map

和 Set 不一样,你没法使用值列表建立 Map;而是使用 Map 的 .set() 方法添加键值。

.set() 方法有两个参数。第一个参数是键,用来引用第二个参数,即值。

要移除键值对,只需使用 .delete() 方法。

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false
m.clear() //Map {}
复制代码

一样,和 Set 相似,你可使用 .clear() 方法从 Map 中删除全部键值对。

提示:若是你使用 .set()Map 中添加键已存在的键值对,不会收到错误,可是该键值对将覆盖 Map 中的现有键值对。此外,若是尝试使用 .delete() 删除 Map 中不存在的键值,不会收到错误,而 Map 会保持不变。

若是成功地删除了键值对,.delete() 方法会返回 true,失败则返回 false.set() 若是成功执行,则返回 Map 对象自己。

Map 也能够接受一个数组做为参数。该数组的成员是一个个表示键值对的数组。

const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
复制代码

事实上,不只仅是数组,任何具备 Iterator 接口、且每一个成员都是一个双元素的数组的数据结构均可以看成Map构造函数的参数。这就是说,SetMap均可以用来生成新的 Map

注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要很是当心。

const map = new Map();

map.set(['a'], 555);
map.get(['a']) // undefined
复制代码

上面代码的setget方法,表面是针对同一个键,但实际上这是两个值,内存地址是不同的,所以get方法没法读取该键,返回undefined

遍历方法

Map 结构原生提供三个遍历器生成函数和一个遍历方法。

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回全部成员的遍历器。
  • forEach():遍历 Map 的全部成员。
const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"
复制代码

上面代码最后的那个例子,表示Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。

map[Symbol.iterator] === map.entries
// true
复制代码

Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...)。

const map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

[...map.keys()]
// [1, 2, 3]

[...map.values()]
// ['one', 'two', 'three']

[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]

[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
复制代码

WeakMap

WeakMap 和普通 Map 很像,可是具备如下关键区别:

  • WeakMap 只能包含对象做为键,
  • WeakMap 没法迭代,意味着没法循环访问,而且
  • WeakMap 没有 .clear() 方法。
  • WeakMap的键名所指向的对象,不计入垃圾回收机制。

你能够像建立普通 Map 那样建立 WeakMap,可是须要使用 WeakMap 构造函数。

const book1 = { title: 'Pride and Prejudice', author: 'Jane Austen' };
const book2 = { title: 'The Catcher in the Rye', author: 'J.D. Salinger' };
const book3 = { title: 'Gulliver's Travels', author: 'Jonathan Swift' }; const library = new WeakMap(); library.set(book1, true); library.set(book2, false); library.set(book3, true); 复制代码

可是若是你尝试添加对象之外的内容做为键,系统将报错!

library.set('The Grapes of Wrath', false); //Uncaught TypeError: Invalid value used as weak map key(…)
复制代码

WeakMapMapAPI 上的区别主要是两个,一是没有遍历操做(即没有key()、values()entries()方法),也没有size属性。由于没有办法列出全部键名,某个键名是否存在彻底不可预测,跟垃圾回收机制是否运行相关。这一刻能够取到键名,下一刻垃圾回收机制忽然运行了,这个键名就没了,为了防止出现不肯定性,就统一规定不能取到键名。二是没法清空,即不支持clear方法。所以,WeakMap只有四个方法可用:get()、set()、has()、delete()


ES5对象与ES6 Maps的异同

相关文章
相关标签/搜索