es6之Set和Map

ES6新对象Set和Map来了,其他对象请鼓掌欢迎👏

为什么要新添加这两种数据结构

Set用来解决集合中每个值只能有唯一的值,所以set中在添加的值的时候涉及值相等判断,-0,+0应被当作不同的值对待,但在最新的 ECMAScript 6规范中这点已被更改,所以Set中-0,+0都是0;虽然NaN!=NaN,但在set中也只能被添加一次。

Map和对象有以下几点不同

  • 对象有prototype,如果给对象实例上添加了和原型上一样的属性值,则访问不到原型上的那个值了
  • 对象的键可以是字符串和SymbolsMap的键可以是任意类型,包括函数,对象或者其他原始类型
  • 可以轻易的通过Mapsize属性来获得Map的大小,要获得对象的大小需要手动编码计数获得

MapSet在新建时都可直接传入可迭代对象,而传入Map中对象的元素必须是键值对的形式。set实例对象和map实例对象都是可迭代对象,通过for...of遍历时,因为es6在设计这两种结构的时候,添加会保存顺序,所以会按添加的顺序遍历出来,相比对象字面量和数组字面量,Map和Set实例提供了更多的方法

Set

set,在数学概念中称为”集合”。集合在我们上初中时候就已经学过,著名的三大特性为确定性,互异性,无序性。es6中的set也刚好有这三种特性,并且也可以轻松实现集合中的交集,并集,差集。

属性和方法

静态属性

  • Set.length:值为0

原型属性

  • set.size:返回set对象中值的个数

原型方法

  • set.add(value):添加指定的值,若重复则不添加,返回set实例对象
  • set.has(value):判断是否有指定的值,返回boolean值
  • set.delete(value):删除指定的值,返回boolean值
  • set.clear():删除所有的值
  • set.keys()/set.values()/set[@@iterator]():将set对象中每个元素的值按插入序取出来作为新的迭代器对象的值并返回
  • set.entries():将set对象中每个元素按插入序取出来组合成[value,value]数组的形式作为新的迭代器对象的值并返回
  • set.forEach(cb[, thisArg]):cb函数中对set对象中的每个值按插入序进行操作,如果传入第二个参数,将作为cb函数中this的值

应用

实现数组的去重

1
2
3
let a = [1,1,2,2,3,3,4,5];
let s = [...new Set(a)]; // Set(5) {1, 2, 3, 4, 5}
// or let s = Array.from(new Set(a))

并集的实现

1
2
3
4
let f = new Set([1,2,3]);
let v = new Set([2,3,4]);
// 通过去重的方式获得两个集合的并集
let union = new Set([...f, ...v]) // Set(4) {1, 2, 3, 4}

交集的实现

1
2
3
4
let f = new Set([1,2,3]);
let v = new Set([2,3,4]);
// 将其中一个集合置为数组,通过数组的filter方法找到另一个集合中有的值得到交集
let intersect = new Set([...f].filter(x => v.has(x))) // Set(2) {2, 3}

差集的实现

1
2
3
4
let f = new Set([1,2,3]);
let v = new Set([2,3,4]);
// 将其中一个集合置为数组,通过数组的filter方法找到另一个集合中没有的值得到差集
let difference = new Set([...f].filter(x => !v.has(x))) // Set(1) {1}

根据上述的例子,我们也看到了Set和Array之间是如何互相转化,利用各自的方法和属性可以巧妙而简洁的写出想要的功能


Map

属性和方法

静态属性

  • Map.length:值为0

原型属性

  • map.size:map实例对象上键值对的个数

原型方法

  • map.get(key):如果能找到的话,返回key对应的value,否则返回undefined
  • map.set(key, value):为map实例添加指定的key-value对,返回map实例对象
  • map.has(key):根据指定的key查找是否有关联的value,返回boolean值
  • map.delete(key):根据指定的key查找是否有关联的value,有的话删除并返回true,没有的话返回false
  • map.clear():清空map实例对象的所有key-value对
  • map.keys():将map对象的每个元素中的key按插入序取出来作为新的迭代器对象的值并返回
  • map.values():将map对象的每个元素中的value按插入序取出来作为新的迭代器对象的值并返回
  • map.entries()/map[@@iterator]():将map对象中每个元素按插入序取出来组合成[key,value]数组的形式作为新的迭代器对象的值并返回
  • map.forEach(cb[, thisArg]):与set方法的forEach使用一样

注意点

set添加值

基本类型

添加基本类型值的时候,如果set方法传入的key相等,会覆盖掉上次的值,这里也同样涉及到值相等的判断,对于基本类型值,只要两个值严格相等,Map将其视为一个键,undefinednull也是两个不同的键。

1
2
3
4
5
let m = new Map();
m.set(0,0); // Map(1) {0 => 0}
m.set(-0,0); // Map(1) {0 => 0}
m.set(true, 1); // Map(2) {0 => 0, true => 1}
m.set('true', 1); // Map(2) {0 => 0, true => 1, 'true' => 1}

引用类型

添加引用类型值的时候,Map的键根据内存地址来判断是否是相等的值,也就是说即使传入了两个空对象,但是这两个对象分配的地址不同,也会被Map添加两次,如果我们使用Map,以对象作为键名,可以避免与其他人的命名冲突(思考:对象中为防止命名冲突,新增了 Symbol 类型,那 Symbol 能用于 Map 对象中的键吗?)

1
2
3
let m = new Map();
m.set({},1); // Map(1) {Object {} => 1}
m.set({},2); // Map(2) {Object {} => 1, Object {} => 2}