数组在JavaScript中的使用正如其他语言的数组一样,但缺少更多类型的集合导致数组也经常被当作队列与栈来使用。
数组只使用了数值型的索引,而如果非数值型的索引是必要的,开发者便会使用非数组的对象。
这种技巧引出了非数组对象的定制实现,即 Set 与 Map。
Set
是一个构造函数
创建:
const s = new Set();
[2, 3, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
//结果会去重
类似数组,值唯一
添加值:add()方法,重复值不会添加
初始化
let set = new Set([1, 2, 3, 4, 4]);
console.log([...set]);
//打印出来是一个数组
let arr = [2, 3, 4, 3, 4, 1, 2];
arr = [...new Set(arr)];//解构赋值,没有...就不是数组
console.log(arr);
//[2, 3, 4, 1]
应用:去重
let items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(...items);
//1 2 3 4 5 打印出来是数字,与上面相比没有[],所以不是数组去重
let str = "abbcddef";
console.log([...new Set(str)].join(""));
//abcdef 用join连接
两个对象是不同的
let set = new Set();
set.add({});
set.add({});
console.log(set.size);
//长度为2,两个不同的空对象
let a = {};
let b = a;
set.add(a);
set.add(b);
console.log(set.size);
//长度为3,加了两个相同空对象a
向Set实例加入值时不会发生类型转换,5和’5’不同
方法:
add(value),添加值,返回该Set实例的引用。
delete(value),删除值,返回一个布尔值,表示删除是否成功。
has(value),返回一个布尔值,表示该值是否是Set实例的成员。
clear( ),清除所有成员,没有返回值。
let set = new Set();
set.add(1).add(2).add(2);
console.log(set);
//Set(2) {1, 2} 自动去重
console.log(set.has(2));
true//存在
console.log(set.has(3));
false//不存在
set.delete(1);
//Set(1) {2} 删除了1只剩2
set.clear();
//Set(0) {} 删除全部成员为空了
遍历方法:
keys( ),返回键名的遍历器。
values( ),返回键值的遍历器。
由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
let set = new Set(["red", "green", "blue"]);
for (let item of set.keys()) {
console.log(item);
}
for (let item of set.values()) {
console.log(item);
}
//Set结构实例的默认遍历器生成函数就是values()方法
for (let item of set) {
console.log(item);
}
//以上三种方法全部显示red green blue
entries( ),返回键值对的遍历器。
迭代对象中数组的索引值作为 key, 数组元素作为 value
let set = new Set([98,98,52]);for (let item of set.entries()) {
console.log(item);
}
[98, 98] //value值=key值
[52, 52]
forEach( ),使用回调函数遍历每个成员,没有返回值。
let set = new Set([41,58,78]);
set.forEach((value, key, s) => {
console.log(s.size);
console.log(value);
});
//3 41 3 58 3 78
结合数组
let ns = new Set([1, 2, 3, 4]);
ns = new Set([...ns].map(x => x * 2));
console.log(ns);
//Set(4) {2, 4, 6, 8}
//map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值
ns = new Set([...ns].filter(x => x % 3 == 0));
console.log(ns);
//Set(1) {6}
//filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
//并集
let union = new Set([...a], [...b]);
console.log(union);
//Set(3) {1, 2, 3}
!!!!!!!!!!!!!!
//Set([...a], [...b])是错误的,应该是Set([...a,...b]),输出为{1,2,3,4}
//交集
let interect = new Set([...a].filter(x => b.has(x)));
console.log(interect);
//Set(2) {2, 3}
//差集
let diffs = new Set([...a].filter(x => !b.has(x)));
console.log(diffs);
//Set(1) {1}
WeakSet
WeakSet的成员只能是对象。
WeakSet中的对象都是弱引用。
垃圾回收机制不考虑WeakSet对该对象的引用。
如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占的内存。
WeakSet 不可遍历
《应用场景》
WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。
只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。
Map
Map类型是键值对的有序列表,而键和值都可以是任意类型。
相较于Object
Object只能用字符串当作键,其他类型作为键时会自动转换为字符串。
字符串-值
Map可以直接使用各种类型的值(包括对象)作为键。
值-值
Object重在表达对象,Map重在表达数据结构–字典。
方法
set(key, value),设置(添加)键值对。
get(key),通过key获取对应值。
类似Set的方法和属性:
has(key)
delete(key)
clear( )
size
let m=new Map();
let o={p:'Hello world'};
m.set(o,'content');
console.log(m.size); //1
console.log(m.get(o)); //content
console.log(m.has(o)); //true
console.log(m.delete(o)); //true
console.log(m.has(o));//false
console.log(m.clear());//undefined
如果Map的键是一个简单类型的值(数值,字符串,布尔值),只要两个值严格相等,Map就认为是同一个键。
let mm = new Map();mm.set(-0, 123);
console.log(mm.get(+0));//123
mm.set(true, 1);
mm.set("true", 2);
console.log(mm.get(true));//1
mm.set(undefined, 3);
mm.set(null, 4);
console.log(mm.get(undefined));//3 null==undefined,但是这里要求绝对相等
map.set(NaN, 111);
console.log(mm.get(NaN));//第一次是111,第二次是未定义,因为NaN自己都不等于自己,但是作为key的话只能出现一次
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员。
Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。
const map = new Map([
["F", "no"],
["T", "yes"]
]);
for (let [key, value] of map.entries()) {
console.log(key, value);
}
//F no T yes entries()方法和map的原生遍历方法相同
for (let [key, value] of map) {
console.log(key, value);
}
//F no T yes
与Set类似,Map也可以利用…(展开运算符)转换为数组,从而利用map( )、filter( )等方法。
const map0 = new Map()
.set(1, "a")
.set(2, "b")
.set(3, "c");
console.log(map0);
//Map(3) {1 => "a", 2 => "b", 3 => "c"}
const map1 = new Map([...map0].filter(([k, v]) => k < 3));
console.log(map1);
//Map(2) {1 => "a", 2 => "b"}
const map2 = new Map([...map0].map(([k, v]) => [k * 2, "_" + v]));
console.log(map2);
//Map(3) {2 => "_a", 4 => "_b", 6 => "_c"}
与其他数据结构的相互转换
Map转换为数组
const myMap = new Map().set(true, 7).set({ foo: 3 }, ["abc"]);
let myArr = [...myMap];
console.log(myArr);
//(2) [Array(2), Array(2)]
//0: (2) [true, 7]
//1: (2) [{…}, Array(1)]
数组转换为Map
const myMap = new Map([
[true, 7],
[{ foo: 3 }, ["abc"]]
]);
console.log(myMap);