ES6学习笔记(五)Set结构和Map结构

本文深入探讨ES6中Set和Map数据结构的特性与应用,包括构建方式、操作方法、遍历技巧及与其他结构的转换。通过实例展示如何利用Set进行数组去重,运用Map实现键值对管理,以及它们在实际编程中的高效使用。

Set


  • Set的构建

Set是ES6中新的数据结构,它与数组类似,但是Set里面没有重复的值,可以用new运算符来构造一个Set,Set的构造函数可以传入一个参数,参数要是有iterable的结构,比如数组,arguments,字符串,元素集合,Set结构,Map结构等

let s1=new Set([1,2,3,4,5]);
let s2=new Set("abcdef");

Set有一个属性size,可以得到Set结构的value个数

let s1=new Set([1,2,3,4,5]);
let s2=new Set([1,2,3,4,5,5]);
console.log(s1.size+"   "+s2.size);

//5   5

上面传入Set的两个数组长度不同,最后返回的size却是一样的,这是因为第二个数组里,5是重复的,所以并没有真的添加进去,所以长度也只有5。

但也有特殊的,比如传入两个{},size会变为2,这是因为Set内部判断两个值是否相等使用的算法类似于===,但有所不同的是在Set中,两个NaN是相同的,所以传入两个NaN后size为1,而因为"5"!==5,所以new Set(["5",5]}的size等于2

new Set([{},{}]).size//2
new Set([NaN,NaN]).size//1
new Set(["5",5]).size;//2
  • Set的操作方法

add(value)

add方法接受一个参数,如果Set结构中已有和参数相同的值,则不做操作,如果没有和参数相同的值,则将该值添加到Set中,最后返回操作后的Set,因此可以通过链式结构来添加多个值。

let s=new Set([1,2,3,4]);

console.log(s.size);//4

s.add(5);

console.log(s.size);//5

s.add(5);

console.log(s.size);//5

s.add(6).add(7).add(8);//返回的还是Set结构,所以可以继续调用add

console.log(s.size);//8

delete(value)

delete方法同样接受一个参数,如果Set结构中已有和参数相同的值,则删除该值,返回true,如果没有相同的值,则返回false,因为返回的是一个布尔值,所以不能使用链式结构删除多个值

let s=new Set([1,2,3,4,5]);

console.log(s.size);//5

s.delete(1);

console.log(s.size);//4

s.delete(1);

console.log(s.size);//4

has(value)

has方法用于判断该Set结构中是否有和参数相同的值,有的话返回true,没有的话返回false,无论有没有都不对数组进行操作。

let s=new Set([1,2,3,4]);

s.has(1);//true

s.has(6);//false

clear()

clear方法不用传入参数,会把整个Set结构清空

let s=new Set([1,2,3,4]);

console.log(s.size);//4

s.clear();

console.log(s.size);//0
  • Set的遍历方法

keys()和values()

keys遍历键名,values遍历键值,但是因为Set没有键名,所以keys和values得到的结果是一样的

let s=new Set([1,2,3]);

for (let item of s.keys()) {
  console.log(item);
}

//1    2     3

for (let item of s.values()) {
  console.log(item);
}

//1    2     3

entries()

entries方法返回键名和键值,因为Set没有键名,所以就返回两个键值

let s=new Set([1,2,3]);

for (let item of s.entries()) {
  console.log(item);
}

//[1, 1]
//[2, 2]
//[3, 3]

forEach()

Set结构的forEach方法和数组的forEach方法一样,参数依次为键名,键值,集合本身(这里为调用该方法的Set实例),因为没有键名,所以这里的键名依然和键值一样。

let s=new Set([1,2,3]);

s.forEach(function(key,val,set){
    console.log(key+"   "+val+"   "+set);
})

//1   1   [object Set]
//4 2   2   [object Set]
//4 3   3   [object Set]
  • Set的使用

1.通过使用扩展运算符或者Array.from方法实现Set结构转化为数组

let s=new Set([1,2,3]);

console.log([...s]);//[1,2,3]

console.log(Array.from(s));//[1,2,3]

2.数组去重

将数组传入Set的构造函数中,再转化为数组,实现数组的去重

let arr=[1,2,3,3,4];

[...new Set(arr)]//[1,2,3,4]

3.实现交集,并集,差集

let arr1=[1,2,3,4];
let arr2=[1,3,5,7];

let union=[...new Set([...arr1,...arr2])];//并集

//[1,2,3,4,5,7]

let intersect = [...new Set(arr1.filter(x => new Set(arr2).has(x)))];//交集
//[1,3]

let dif = [...new Set(arr1.filter(x => !new Set(arr2).has(x)))];
//[2,4]


  • WeakSet

与Set结构类似,但是WeakSet的成员只能是对象,而且WeakSet中的对象都是弱引用,没有被垃圾回收机制考虑,如果WeakSet外部对该对象的引用消失,该对象会被垃圾回收机制回收,WeakSet中的该对象就会消失。

WeakSet有三个操作方法,add(value),delete(value),has(value),使用方法和Set中对应的这三个方法一样,但是要传入对象参数,WeakSet没有size属性和forEach,无法进行遍历。

let ws=new WeakSet([{1:2},{2:3}]);

 

Map


  • Map的构造

在ES5的对象中,键名必须为字符串,否则会被自动转化为字符串,而通过使用Map,我们可以使用任意结构的键名,使用Map构造函数声明一个Map结构时,传入一个数组参数,数组里面是长度为2的多个数组,内层数组第一个值为键名,第二个值为键值,如果键名相同,那么后写入的会覆盖先写入的

let m=new Map([[1,2],[true,false],[{a:1},{b:2}],[[1,2,3],[4,5,6]]]);
let k1='111';
let k2='111';
let m1=new Map([[k1,1],[k2,2]]);
m1.get(k1)//1
m1.get(k2)//2

Map上的键实际上是跟内存地址绑定的,只要内存地址不同,就视为两个键,所以上面代码中虽然k1==k2,但是内存地址是不同的,所以视为两个键。

  • Map的操作方法

get(value)和set(value)

get方法传入一个参数,参数即为key,若Map中有对应的key的话就返回对应的value,没有的话返回undefined;set方法传入两个参数,第一个为key,第二个为value,如果Map中有对应的key,则修改对应的value,如果没有的就添加新的key和value,返回新得到的Map(可以使用链式结构多次添加)

let m=new Map([[1,"first"],[2,"second"]]);

m.get(1);//first

m.set(3,"third");

delete,has,clear的用法和Set的用法是一样的,delete和has通过键名来查找对应的值,如下代码

let m=new Map([[1,"first"],[2,"second"]]);

console.log(m.size);//2

m.delete(1);

console.log(m.size);//1

m.has(2);//true

console.log(m.size);//1

m.clear();

console.log(m.size)//0
  • Map的遍历方法

forEach,values,keys,entries

forEach即遍历Map中对应的key和value

而values和keys顾名思义就是遍历Map中所有的value和key,使用for of来遍历

entries则同时遍历value和key,同时可以取得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"
  • 与其他结构的相互转化

1.Map转数组

使用扩展运算符

let m=new Map([[1,"first"],[2,"second"]]);
[...m]

2.数组转Map

直接将数组传入Map构造函数中

new Map([
  [1, "first"],
  [2, "second"]
])

3.Map转对象

如果Map中所有key都为字符串,则可以直接转为对象,如果有非字符串,则强制转为字符串再转为对象

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 }

此外也可以采用ES10的Object.fromEntries

var m=new Map([[1,"first"],[2,"second"]])
Object.fromEntries(m)
// {1: "first", 2: "second"}

4.对象转Map

使用set方法将对应的key和value传入

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可以用ES7的Object.entries

var obj ={
    1: "first",
    2: "second"
}
var m = new Map(Object.entries(obj))
// Map(2) {"1" => "first", "2" => "second"}

5.Map转为json

如果Map中的key都为字符串,那么转为对象json,如果有非字符串的key,则转为数组json

function strMapToJson(strMap) {
  return JSON.stringify(strMapToObj(strMap));
}

let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'



function mapToArrayJson(map) {
  return JSON.stringify([...map]);
}

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'

6.json转为Map

使用json.parse将json转为对象后,使用对象转Map的方法转为Map

function jsonToStrMap(jsonStr) {
  return objToStrMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}

function jsonToMap(jsonStr) {
  return new Map(JSON.parse(jsonStr));
}

jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}
  • WeakMap

WeakMap只接受对象作为键名(null除外),其他的类型作为键名都会报错

作为键名的对象不计入垃圾回收机制,是弱引用,与WeakSet的成员一样,如果外部的引用消失的话,该对象也会被清除,但要注意的是,只有键名是弱引用,键值并不是弱引用,键值仍会计入垃圾回收机制,如果外部引用消失,键值并不会消失。


参考自阮一峰的《ECMAScript6入门》


ES6学习笔记目录(持续更新中)

 

ES6学习笔记(一)let和const

ES6学习笔记(二)参数的默认值和rest参数

ES6学习笔记(三)箭头函数简化数组函数的使用

ES6学习笔记(四)解构赋值

ES6学习笔记(五)Set结构和Map结构

ES6学习笔记(六)Iterator接口

ES6学习笔记(七)数组的扩展

ES6学习笔记(八)字符串的扩展

ES6学习笔记(九)数值的扩展

ES6学习笔记(十)对象的扩展

ES6学习笔记(十一)Symbol

ES6学习笔记(十二)Proxy代理

ES6学习笔记(十三)Reflect对象

ES6学习笔记(十四)Promise对象

ES6学习笔记(十五)Generator函数

ES6学习笔记(十六)asnyc函数

ES6学习笔记(十七)类class

ES6学习笔记(十八)尾调用优化

ES6学习笔记(十九)正则的扩展

ES6学习笔记(二十)ES Module的语法

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值