ღ( ´・ᴗ・` )每天告诉自己编程是世界上最简单的事情,是不是很开心ღ
每当需要ES6语法的时候,都要去查阅阮一峰老师写的【ES6入门】,强烈推荐,传送门:http://es6.ruanyifeng.com/。
对于Set的用法仅仅停留在数组去重这个层面上,例如仅仅知道通过Array.from(new Set([1,2,4,5,6,4]) )可以实现数组去重,对Map的了解更是知之甚少。而且并不知道深层原因是什么?并且不明白实现Set和Map这两种数据结构,目的是什么,意义又是什么呢??没有认真研究这一部分的时候,一直很困惑。所以认真去查阅了资料,通过查阅资料了解到:Set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构。Set和Map主要的应用场景在于数组去重和数据存储。
接下来,请随我一起徜徉在Set和Map这两种数据结构的知识海洋里吧!!!
一、Set数据结构
1、基本用法
我们在前面刚刚提到Set是一个集合?它又有什么功能呢?它和数组有什么不同之处呢?下面我们带着疑问一起来看一下:
-
集合是由一组无序且唯一(即不能重复)的项组成的,可以想象成集合是一个既没有重复元素,也没有顺序概念的数组
-
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
-
Set 本身是一个构造函数,用来生成 Set 数据结构
-
这里说的Set其实就是我们所要讲到的集合,先来看下基础用法:
let array = [1, 2, 5, 4, 7];console.log(array);
arr的输出结果:
let set= new Set([1, 2, 5, 4, 7]);console.log(set);
set的输出结果:
从上面两者输出的内容就可以看出他们的不同:
因为他没有我们数组的长度(length),只有个数(size),把个体放进一个花括号内,因此称呼Set为一个集合。
set集合里面的元素不会重复,也就是唯一的,默认的值是value值,没有key。
2、 Set 实例的属性和方法
2.1 Set 结构的实例有以下属性:
Set.prototype.constructor:构造函数,默认就是Set函数。
Set.prototype.size:返回Set实例的成员总数。
2.2 Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
2.2.1操作方法
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
属性和方法的例子如下:
let s = new Set(); //Set本身是一个构造函数,用来生成 Set 数据结构
s.add(1).add(2).add(2);// 注意2被加入了两次结果,但是 Set 结构不会添加重复的值
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false -
Set结构判断是否包括一个键值的方法:
// Set的写法
const properties = new Set();
properties.add(‘width’);
properties.add(‘height’);
if (properties.has(someName)) {
// do something
}
与Object结构的判断方法是不同的,Object结构的判断方法如下:
// 对象的写法
const properties = {
‘width’: 1,
‘height’: 1
};
if (properties[someName]) {
// do something
} -
Array.from方法可以将 Set 结构转为数组
前文提到过,Set数据结构不是数组,而是一个集合。下面这个知识点就是告诉大家,可以通过Array.from方法将Set 结构转为数组(关于Array.from,参考之前写的一篇“数组的扩展”章节,本节不做赘述)。
实例如下:
2.2.2 遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员。
keys( ):返回键名的遍历器
values( ):返回键值的遍历器
entries( ):返回键值对的遍历器
forEach( ):使用回调函数遍历每个成员 -
keys( )、values( )、entries( )
keys方法、values方法、entries方法返回的都是遍历器对象。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为是一致的。
举例如下:
let set = new Set([‘red’, ‘green’, ‘blue’]);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// bluefor (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方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。
-
可以直接用for…of循环遍历 Set
因为Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。因此,可以省略values方法,直接用for…of循环遍历 Set。
举例如下:
let set = new Set([‘red’, ‘green’, ‘blue’]);for (let x of set) { console.log(x); } // red // green // blue
-
forEach( )
Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。let set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value)) // 1 : 1 // 4 : 4 // 9 : 9
该函数的参数与数组的forEach一致,依次为键值、键名、集合本身。forEach方法还可以有第二个参数,表示绑定处理函数内部的this对象。
3、应用
-
Array 与 Set 相互转换
var myArray = [“value1”, “value2”, “value3”];
// 用Set构造器将Array转换为Set
var mySet = new Set(myArray);
mySet.has(“value1”); // returns true// 用...(扩展运算符)操作符将Set转换为Array console.log([...mySet]); // 与myArray完全一致
-
数组去重
let arr = [3, 5, 2, 2, 5, 5];
// 用数组静态方法 Array.from
let unique =Array.from(new Set(arr));// [3, 5, 2]
// 或用扩展运算符(…)
let unique = […new Set(arr)];// [3, 5, 2] -
字符串转成 Set
var text = “happy”;
var stringSet = new Set(text);
console.log(sringSet); // Set(5) {“h”, “a”, “p”, “p”, “y”}
stringSet.size; //5 -
实现并集、交集、差集
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([…a].filter (x => !b.has(x))); // Set { 1 }
二、Map数据结构
1.基本用法
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
一个Map对象以插入顺序迭代其元素 — 一个 for…of 循环为每次迭代返回一个[key,value]数组。
使用方式:new Map([iterable])
参数 iterable:可以是一个数组或者其它的 iterable 对象,其元素或为键值对,或为两个元素的数组。
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
上面的例子展示了如何向 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 map = new Map();
map
.set(1, 'aaa')
.set(1, 'bbb');
map.get(1) // "bbb"
注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
上面代码的set和get方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get方法无法读取该键,返回undefined。
**由上可知,Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。**这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
2、Map 实例的属性和方法
2.1 属性
-
size 属性
size属性返回 Map 结构的成员总数。const map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 2
-
set(key, value)
set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。const m = new Map(); m.set('edition', 6) // 键是字符串 m.set(262, 'standard') // 键是数值 m.set(undefined, 'nah') // 键是 undefined
set方法返回的是当前的Map对象,因此可以采用链式写法。
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
-
get(key)
get方法读取key对应的键值,如果找不到key,返回 undefined。const m = new Map(); const hello = function() {console.log('hello');}; m.set(hello, 'Hello ES6!') // 键是函数 m.get(hello) // Hello ES6!
-
has(key)
has方法返回一个布尔值,表示某个键是否在当前 Map 对象 之中。
const m = new Map();m.set('edition', 6); m.set(262, 'standard'); m.set(undefined, 'nah'); m.has('edition') // true m.has('years') // false m.has(262) // true m.has(undefined) // true
-
delete(key)
delete方法删除某个键,返回true。如果删除失败,返回false。
const m = new Map();
m.set(undefined, ‘nah’);
m.has(undefined) // truem.delete(undefined) m.has(undefined) // false
-
clear()
clear方法清除所有成员,没有返回值。
let map = new Map();
map.set(‘foo’, true);
map.set(‘bar’, false);map.size // 2 map.clear() map.size // 0
2.2 遍历方法
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 [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
Map 结构转为数组结构,比较快速的方法是使用扩展运算符(…)。举例如下:
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤。跟Set数据结构利用该方法类似。
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 还有一个forEach方法,与数组的forEach方法类似,也可以实现遍历。
3.应用
-
Map 与 数组之间的相互转换
// Map 转数组
var myMap = new Map();
myMap.set(“bar”, “foo”);
myMap.set(1, “bar”);
[…myMap]; // [ [“bar”, “foo”], [1, “bar”] ]// 数组转Map const arr = new Map( [ ["bar", "foo"], [1, "bar"] ]); console.log(arr); // Map {"bar" => "foo", 1 => "bar"}
-
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();
myMap.set(“bar”, “foo”)
.set(1, “ooo”);strMapToObj(myMap ); // Object {1: "ooo", bar: "foo"} // 对象转 Map function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } objToStrMap({1: "ooo", bar: "foo"}); // Map {"1" => "ooo", "bar" => "foo"}
-
Map 与 JSON 相互转换
// Map 转 JSON
// Map 的键名为字符串
function strMapToJson(jsonStr) {
return JSON.stringify(strMapToObj(jsonStr));
}
const myMap = new Map();
myMap.set(“bar”, “foo”)
.set(1, “ooo”);
strMapToJson(myMap); // “{“1”:“ooo”,“bar”:“foo”}”// Map 的键名为非字符串 function mapToArrayJson(map) { return JSON.stringify([...map]); } mapToArrayJson(myMap); // "[["bar","foo"],[1,"ooo"]]" // Json 转 Map // 正常情况下所有键名都为字符串 function jsonToStrMap(jsonStr) { return objToStrMap(JSON.parse(jsonStr)); } jsonToStrMap("{"1":"ooo","bar":"foo"}"); // Map {"1" => "ooo", "bar" => "foo"} // 整个JSON 是数组 function jsonToMap(jsronStr) { return new Map(JSON.parse(jsronStr)); } jsonToMap([["bar","foo"],[1,"ooo"]]); // Map {"1" => "ooo", "bar" => "foo"}
Map数据结构是具备json的所有功能,还多出了命名是任意类型,所以Map就是强化版的json。
以上就是这一章节的知识点啦!
哇,又到了跟大家说再见的时候了,其实Set和Map数据结构在属性和方法上有很多类似点,希望大家能多多消化这一块的知识,我们下次见呀。
ღ( ´・ᴗ・` )每天告诉自己编程是世界上最简单的事情,是不是很开心ღ