ES6学习笔记之Set和Map

Set

基本用法

ES6 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。
Set 自己是一个构造函数,用来生成 Set 数据结构。数组

const setArr = new Set();

[1,2,3,4,2,2,3,4].forEach(x => setArr.add(x));

for (let i of setArr) {
  console.log(i);
}
// 1,2,3,4
复制代码

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

  1. 以数组为参数, 能够去重
  2. Set 加入值的时候,不会发生类型转换,因此4和"4"是两个不一样的值。
//  以数组为参数
const set = new Set([1, 2, 3, 4, 4, '4'])
[...set]  // [1, 2, 3, 4, '4']
set.size  // 5

// 一个相似数组的带 iterable 接口的对象
const set = new Set(document.querySelectorAll('div'))

复制代码

Set结构转换成数组有两个简单的方法数据结构

[...set]  // [1, 2, 3, 4, '4']

Array.from(set)  // [1, 2, 3, 4, '4']
复制代码

Set 实例的属性和方法

Set 结构的实例有如下属性。函数

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。

Set 实例的方法分为两大类:操做方法(用于操做数据)和遍历方法(用于遍历成员)。ui

四个操做方法。

  1. add(value):添加某个值,返回 Set 结构自己。
  2. delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  3. has(value):返回一个布尔值,表示该值是否为Set的成员。
  4. clear():清除全部成员,没有返回值。
const s = new Set()
s.add(1).add(2).add(2)

s.size // 2

s.has(1) // true
s.has(2) // true
s.has(3) // false

s.delete(2)
s.size  // 1
s.has(2) // false

复制代码

四个遍历方法

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

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

const set = new Set(['a', 'b', 'c']);

for (let item of set.keys()) {
  console.log(item);
}
// a
// b
// c

for (let item of set.values()) {
  console.log(item);
}
// a
// b
// c

for (let item of set.entries()) {
  console.log(item);
}
// ["a", "a"]
// ["b", "b"]
// ["c", "c"]

复制代码

对象结构 在使用这个几个方法的时候相似,但有必定区别spa

const obj = { 1: 'a', 2: 'b', 3: 'c' }
for (let item of Object.keys(obj)) {
  console.log(item);
}
// 1
// 2
// 3

for (let item of Object.values(obj)) {
  console.log(item);
}
// a
// b
// c

for (let item of Object.entries(obj)) {
  console.log(item);
}
// [1, "a"]
// [2, "b"]
// [3, "c"]
复制代码

Set 结构的实例默承认遍历,它的默认遍历器生成函数就是它的values方法。
这意味着,能够省略values方法,直接用for...of循环遍历 Setprototype

for (let i of set) {
  console.log(i)
}
// a
// b
// c

复制代码

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

set.forEach((value, key) => console.log(key + ' : ' + value))
// a: a
// b: b
// c: c
复制代码

遍历的应用对象

  1. 去重
  2. 实现并集(Union)、交集(Intersect)和差集(Difference)
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...union].filter(x => !intersect.has(x)));
// Set {1}
复制代码

若是想在遍历操做中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。

// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6

// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6
复制代码

WeakSet

WeakSet 结构与 Set 相似,也是不重复的值的集合。可是,它与 Set 有两个区别。

(1):WeakSet 能够接受数组和相似数组的对象做为参数。该数组的全部成员都会自动成为WeakSet的实例对象的成员。数组成员只能是对象,不能是其余类型的值。不然报错。

const a = [[1, 2], [3, 4], {a: 1}]
const ws = new WeakSet(a)
// WeakSet {[1, 2], [3, 4]}

const b = [1, 2, [1,2]]
new WeakSet(b)  // Uncaught TypeError: Invalid value used in weak set
复制代码

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

Map

含义和基本用法

一个常规的对象本质应该是键值对的合集(即Hash结构)。它的键应该是一个字符串。 可是有时候须要使用其余类型好比对象来作 键值对的 键。 因而就有了Map结构

const data = {};
const element = document.getElementsByTagName('div')

data[element] = 'div';
data[element]  //  "div"
data['[object HTMLCollection]'] // "div"

// {[object HTMLCollection]: "div"}

const elementSpan = document.getElementsByTagName('span')
data[elementSpan] //  "div"

复制代码

element 被转化成了'[object HTMLCollection]' 只是个字符串。 并不能达到经过element 取到值的效果。可是,使用Map结构能够。 Map结构与对象很类似也是键值对, 但Map 的键能够不是字符串,能够是各类类型的值(包括对象)。若是你须要“键值对”的数据结构,MapObject 更合适。

const dataMap = new Map()
dataMap.set(element, 'div')
dataMap.get(element)  // div

//  has,delete
dataMap.has(element)    //  true
dataMap.delete(element) //  true
dataMap.has(element)    //  false

复制代码

以上 new Map() 实例对象为一个Map结构,提供了setgethasdelete几个方法。轻松了实现了增删改查。 构造函数Map 能够接受一个数组做为参数。

const map = new Map([ ['key1', 'value1'], ['key2', 'value2'] ])

复制代码

任何具备 Iterator 接口、且每一个成员都是一个双元素的数组的数据结构均可以看成Map构造函数的参数。如:数组,Set结构, Map结构。

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

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

注意:只有对同一个对象的引用,Map 结构才将其视为同一个键。

const k1 = {a: 1 }
const k2 = {a: 1 }
const map = new Map()

map.set(k1, 111)
map.get(k2) // undefined

map.set(k2, 222)
map.get(k1) //  111
map.get(k2) //  222
复制代码

由于即使值相同,可是两个对象内存地址是不同的。

这就解决了同名属性碰撞(clash)的问题,咱们扩展别人的库的时候,若是使用对象做为键名,就不用担忧本身的属性与原做者的属性同名。

若是 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,好比0和-0就是一个键。

实例的属性和操做方法

  1. set(key, value): set方法设置键名key对应的键值为value,而后返回整个 Map 结构。若是key已经有值,则键值会被更新,不然就新生成该键。
  2. get(key):get方法读取key对应的键值,若是找不到key,返回undefined
  3. size 属性: 返回 Map 结构的成员总数。
  4. has(key): has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
  5. delete(key): delete方法删除某个键,返回true。若是删除失败,返回false
  6. clear(): clear方法清除全部成员,没有返回值。
const map = new Map();
//  能够采用链式写法。
map.set('a', 1).set('b', 2)

map.size          // 2

map.get('b')      // 2
map.set('b', 222)
map.get('b')      // 222
map.get('c')      // undefined

map.has('b')      // true

map.delete('b')   // true
map.has('b')      // false

map.clear()
map.size          // 0
复制代码

遍历方法

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

须要特别注意的是,Map 的遍历顺序就是插入顺序。

const map = new Map([['a', 1], ['b',  2]]);

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

for (let value of map.values()) {
  console.log(value);
}
// 1
// 2

for (let item of map.entries()) {
  console.log(item);
}
// ["a", 1]
// ["b", 2]

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "a" 1
// "b" 2

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "a" 1
// "b" 2
复制代码

与其余数据结构的互相转换

(1)Map 与数组的互换

const arr1 = [[{'k1': 11}, 11 ],['a', 1]]
const map = new Map(arr1)

[...map]          // [[{'k1': 11}, 11 ],['a', 1]]
Array.from(map)   // [[{'k1': 11}, 11 ],['a', 1]]
复制代码

(2) Map 与对象的互换

若是全部 Map 的键都是字符串,它能够转为对象。

const obj1 = { a:1, b:2 }
const obj2 = {}
const map = new Map()

for(let key of Object.keys(obj1)) {
  map.set(key, obj1[key])
}

console.log(map)    // Map(2) {"a" => 1, "b" => 2}

for (let [key,value] of map) {
  obj2[key] = value
}

console.log(obj2)  // {a: 1, b: 2}
复制代码

(3) JSON 要转换成 Map 能够先转换成数组或者对象,而后再转换。

WeakMap

WeakMap结构与Map结构相似,也是用于生成键值对的集合。

const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"
复制代码

WeakMap与Map的区别有两点。

  1. WeakMap只接受对象做为键名(null除外),不接受其余类型的值做为键名。
  2. WeakMap的键名所指向的对象,不计入垃圾回收机制。
const map = new WeakMap();
map.set(1, 2)
// TypeError: 1 is not an object!

复制代码

WeakMap 的语法:

WeakMapMap 类似但有两个区别:

  1. 没有遍历操做(即没有key()、values()和entries()方法)。
  2. 没法清空,即不支持clear方法。 所以,WeakMap只有四个方法可用:get()、set()、has()、delete()。

WeakMap 的用途

WeakMap 应用的典型场合就是 DOM 节点做为键名。 WeakMap 的另外一个用处是部署私有属性。

const _counter = new WeakMap();
const _action = new WeakMap();

class Countdown {
  constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
  }
  dec() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
      _action.get(this)();
    }
  }
}

const c = new Countdown(2, () => console.log('DONE'));

c.dec()
c.dec()

复制代码

习题: 一· 求set 的值

let arr1 = [1, 2, 3, '3', 2]
const set = new Set(arr1)
复制代码

二. 求遍历的输出

for (let [key, value] of set.entries()) {
  console.log(key === value)
}
复制代码

三. set 转换数成数组
四. 求 data[obj1],

const obj1 = { a: 1}
const obj2 = { b: 2}

const data = {}
data[obj1] = 11
data[obj2] = 22

// 求 data[obj1]

复制代码

五. 1.求 map.get({a: 1}), 2.若是 map 要转换成其余结果,应该是对象仍是数组

const map = new Map()
map.set({a: 1}, 111)

map.get({a: 1})


复制代码
相关文章
相关标签/搜索