你不知道的ES6系列: 加强版Array、Object之Set、Map数据结构

前言

笔者最近在深刻学习ES6中, 在写代码的过程当中用到了set,Map这两个ES6新增的数据结构, 深切感觉到了它们的强大, 解决问题的能力。因而笔者决定写一篇文章总结一下。并分享给你们,但愿可以对你们有帮助。这篇文章主要给你们介绍了关于ES6学习笔记之map、set与数组、对象对比的相关资料, 下面让咱们一块儿走进set, Map的世界吧😄😄。html

舒适提示: Map和Set是ES6标准新增的数据类型,请根据浏览器的支持状况决定是否要使用。前端

一、Map解析

JavaScript的默认对象表示方式{}能够视为其余语言中的Map或Dictionary的数据结构,即一组键值对。 可是JavaScript的对象{}有个小问题,就是键必须是字符串。但实际上Number或者其余数据类型做为键也是很是合理的。 为了解决这个问题,最新的ES6规范引入了新的数据类型Map。git

Object和Map对比

Object是字符串-值,Map是值-值es6

  • Object键为string类型,Map的键是任意类型
  • 手动计算Object尺寸,Map.size能够获取尺寸
  • Map的排序是插入顺序
  • Object有原型,因此映射中有一些缺省的键。能够理解为Map=Object.create(null)

基本用法

// 初始化
let key1 = 12;
let value1 = 'test';
let m1 = new Map(); // m1: Map {}
let m2 = new Map([[key1, value1]]); // m2: Map { 12 => 'test' }
复制代码

实例的属性

一、Map.size() 返回对象中所包含的元素个数
let m2 = new Map([['ou', 'yang'],['xing', 'hua']]);
console.log(m2.size) //输出2
复制代码

5种操做方法

一、set(key, value) 向对象中添加新元素
var m = new Map(); // 空Map
m.set('ouyang', 21); // 添加新的key-value
console.log(m); //Map { 'ouyang' => 21 }
m.set('ouyang', 22)
console.log(m); //Map { 'ouyang' => 22 }
复制代码

注意:因为一个key只能对应一个value,因此,屡次对一个key放入value,后面的值会把前面的值冲掉。github

二、get(key) 经过键值查找特定的数值并返回
let m2 = new Map([[12, 'yang']])
console.log(m2.get(12)) // 返回yang
复制代码
三、has(key) 若是键存在则返回true,不然false
let m2 = new Map([[12, 'yang']])
console.log(m2.has(12)); // true
console.log(m2.has(13)); // false
复制代码
四、delete(key) 经过键值移除相应的value值
let m2 = new Map([[12, 'yang']])
m2.delete(12)
console.log(m2) // Map {}
复制代码
五、clear() 将这个字典中的全部元素删除
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
m2.clear()
console.log(m2) // Map {}
复制代码

4种遍历方法

在此以前有必要说一下迭代器的做用, 迭代器是一种特殊对象,这种对象具备如下特色:数组

1,全部对象都有一个next方法 2,每次调用next方法,都会返回一个对象,该对象包含两个属性,一个是value, 表示下一个将要返回的值。另外一个是done,他是一个布尔值,用来表示该迭代器是否还有数据能够返回. 3,迭代器还会保存一个内部指针指向当前集合中的值浏览器

想深刻了解迭代器的点这里bash

一、keys() 返回一个新的 Iterator 对象。它包含按照顺序插入 Map 对象中每一个元素的key值。
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
var iterator1 = m2.keys(); 
console.log(iterator1); //[Map Iterator] { 12, 'xing' }
console.log(iterator1.next().value); // 12
console.log(iterator1.next().value); // xing
复制代码
二、values() 方法返回一个新的Iterator对象。它包含按顺序插入Map对象中每一个元素的value值。
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
console.log(m2.values()) // [Map Iterator] { 'yang', 'hua' }
复制代码
三、entries() 方法返回一个新的包含 [key, value]的 Iterator对象,返回的迭代器的迭代顺序与 Map对象的插入顺序相同。
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
var iterator1 = m2.entries(); 
console.log(iterator1); //[Map Iterator] { [ 12, 'yang' ], [ 'xing', 'hua' ] }
复制代码
四、forEach() 方法将会以插入顺序对 Map 对象中的每个键值对执行一次参数中提供的回调函数。

myMap.forEach(callback[, thisArg])数据结构

1.必要参数, callback为每一个元素所要执行的函数。 2.可选参数, callback执行时其 this 的值。函数

callback 函数有三个参数

1.value -元素的值 2.key -元素的键 3.Map -当前正在被遍历的对象

下面请看看例子

let Data = new Map();
let objKey = { num:10 };
let obj = {
  num:5
}
Data.set("a", 1);
Data.set("b", 2);
Data.set("c", 3);
Data.forEach(function (value,key) {
  console.log(value*this.num); // 输出5 10 15
}, obj)
复制代码

从上面的代码能够看出回调函数中this指向obj对象。

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

1. Map 转为数组

Map 转为数组最方便的方法,就是使用扩展运算符(...)。

const myMap = new Map()
   .set(true, 7)
   .set({foo: 3}, ['abc']);
    [...myMap]  // 输出 [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
复制代码
2. 数组 转为 Map

将数组传入 Map 构造函数,就能够转为 Map。

new Map([
   [true, 7],
   [{foo: 3}, ['abc']]
 ])
 // Map {
 //   true => 7,
 //   Object {foo: 3} => ['abc']
 // }
复制代码
3. Map 转为对象

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

function strMapToObj(strMap) {
   let obj = Object.create(null);
   for (let [k,v] of strMap) {
     obj[k] = v;
   }
   return obj;
 }
 
 const myMap = new Map()
   .set('yes', true)
   .set('no', false);
 strMapToObj(myMap)
 // { yes: true, no: false }
复制代码

若是有非字符串的键名,那么这个键名会被转成字符串,再做为对象的键名。

4. 对象转为 Map
function objToStrMap(obj) {
   let strMap = new Map();
   for (let k of Object.keys(obj)) {
     strMap.set(k, obj[k]);
   }
   return strMap;
 }
 
 objToStrMap({yes: true, no: false})
 // Map {"yes" => true, "no" => false}
复制代码

结合数组的map方法、filter方法,能够实现 Map 的遍历和过滤(Map 自己没有map和filter方法)。

const map0 = new Map()
   .set(1, 'a')
   .set(2, 'b')
   .set(3, 'c');
 
 const map1 = new Map(
   [...map0].filter(([k, v]) => k < 3)
 );
 // 产生 Map 结构 {1 => 'a', 2 => 'b'}
 
 const map2 = new Map(
   [...map0].map(([k, v]) => [k * 2, '_' + v])
     );
 // 产生 Map 结构 {2 => '_a', 4 => '_b', 6 => '_c'}
复制代码

写到这里,Map的基本用法和应用以及讲完了,我相信应该能够知足你们平常使用的需求吧。下面来看看set数据结构吧~~

二、Set解析

  1. ES6 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。Set 自己是一个构造函数,用来生成Set数据结构。
  2. Set和Map相似,也是一组key的集合,但不存储value。因为key不能重复,因此,在Set中,没有重复的key。

Array和Set对比

都是一个存储多值的容器,二者能够互相转换,可是在使用场景上有区别。以下:

  • Array的indexOf方法比Set的has方法效率低下
  • Set不含有重复值(能够利用这个特性实现对一个数组的去重)
  • Set经过delete方法删除某个值,而Array只能经过splice。二者的使用方便程度前者更优
  • Array的不少新方法map、filter、some、every等是Set没有的(可是经过二者能够互相转换来使用)

基本用法

const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)) //添加元素;
for (let i of s) {
  console.log(i);
}
// 2 3 5 4
复制代码

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

  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 实例的属性和方法和Map 实例的属性和方法相似,下面简要介绍一下。

实例的属性

一、Set.size() 返回对象中所包含的元素个数
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)) //添加元素;
console.log(s.size) // 由于不能有重复元素因此输出4
复制代码

4种操做方法

因为Set只有key,并不存储value(或者说键名和键值是同一个值),因此相对Map少了get()方法

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

s.size // 2

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

s.delete(2)
console.log(s) //Set { 1 }
s.size  // 1
s.has(2) // false
复制代码

4种遍历方法

方法名和Map的遍历方法名同样,下面来看用法

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

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

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"]
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
复制代码
注意: Set使用上须要警戒的地方

因为Set中元素的独一无二,根据内存地址来进行判断,因此若是有多个元素是引用型的话,尽管值相同,可是内存地址不一样,那么在Set对象中也将会存储多份,和Map相似

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

1. Set集合转化Array数组
let set = new Set([1, 2, 3, 3, 4]);
let arr = Array.from(set)  //输出[1,2,3,4]
复制代码
2. Array数组转Set集合
var arr = [55, 44, 65];
var set = new Set(arr);
console.log(set) // Set { 55, 44, 65 }
console.log(set.size === arr.length);
console.log(set.has(65));
复制代码

总结

经过笔者的解析,发现Set、Map这两种ES6新增的数据结构在必定程度解决了Object和Array的一些痛点。他让咱们在选择数据结构去解决具体问题时又多了两种选择,在开发过程当中,涉及到数据结构,能使用Map不使用数组,尤为是复杂的数据结构。若是数据的要求比较高,强调惟一性,就使用Set,放弃使用Object作存储。在数据结构中优先考虑Map、Set,放弃数组和Object。不过何时使用仍是要看应用场景的啦😄。但愿这篇文章可以对你们有所帮助😄。

最后允许小生附上个人github地址 里面记录了我学习前端的点点滴滴,以为有帮助的小哥哥小姐姐能够给个小星星哟😄

参考文献
Javascript官方文档
廖雪峰的官方网站
set和Map阮一峰教程

相关文章
相关标签/搜索