# 1、Set
类似于数组,但是成员的值都是唯一的,没有重复的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===
),主要的区别是向 Set 加入值时认为NaN
等于自身,而精确相等运算符认为NaN
不等于自身。
# 1.1 基础使用
const set1 = new Set([1, 2, 3, 2]);
console.log(set1.size);
const set2 = new Set('abcdeab');
console.log(set2.size, set2);
Set.prototype.constructor
:构造函数,默认就是Set
函数。参数可以传入能迭代的数组或者字符串或者类数组。Set.prototype.size
:返回Set
实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
Set.prototype.add(value)
:添加某个值,返回 Set 结构本身。Set.prototype.delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value)
:返回一个布尔值,表示该值是否为Set
的成员。Set.prototype.clear()
:清除所有成员,没有返回值。
# 1.2 和数组的互相转换
数组转换为set直接使用new Set(Array)
构造即可
set转为数组可以使用扩展运算符[...set]
或者 Array.from(set)
# 1.3 遍历set
keys() values() entries()
这些都是可用的,最直接使用的遍历方式就是 for...of
和forEach
Set的键名就是键值
const set = new Set();
set.add(1);
set.add(2);
for (let val of set) {
console.log(val);
}
// 1
// 2
set.forEach(val => {
console.log(val);
});
// 1
// 2
# 1.4 WeakSet
弱引用类型,只能添加对象,引用计数不会增加。而且可能随时被回收。
个人建议还是使用Set, 用完之后记得调用Set.prototype.clear()
# 2、Map
键值对数据结构,键不再局限于基本数据类型
# 2.1 基础使用
- size属性 返回结构成员总数
- Map.prototype.set(key, value)
- Map.prototype.get(key)
- Map.prototype.has(key)
- Map.prototype.delete(key)
- Map.prototype.clear()
# 2.2 遍历Map
Map.prototype.keys()
:返回键名的遍历器。Map.prototype.values()
:返回键值的遍历器。Map.prototype.entries()
:返回所有成员的遍历器。Map.prototype.forEach()
:遍历 Map 的所有成员。Ï
Map 的遍历顺序就是插入顺序。
const map = new Map();
map.set('a', 1);
map.set('b', 2);
for (let key of map.keys()) {
console.log(key); // a b
}
for (let value of map.values()) {
console.log(value); // 1 2
}
for (let entry of map.entries()) {
console.log(entry); // [ 'a', 1 ] , [ 'b', 2 ]
console.log(entry[0]); // a b
console.log(entry[1]); // 1 2
}
map.forEach((value, key) => {
console.log(value, key);
// 1 a
// 2 b
});
# 2.3 WeakMap
键名只能是对象,保持弱引用。
# 3、for...of
遍历器(Iterator)主要就是为各种不同的数据集合结构提供统一的访问机制。这种机制主要服务于for...of
。
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator
属性,或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator
,它是一个表达式,返回Symbol
对象的iterator
属性,这是一个预定义好的、类型为 Symbol 的特殊值,
TIP
凡是部署了Symbol.iterator
属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象,该对象的根本特征就是具有next
方法。每次调用next
方法,都会返回一个代表当前成员的信息对象,具有value
和done
两个属性。
const arr = [1, 2, 3, 4];
const iter = arr[Symbol.iterator](); // 获取迭代器对象, 对象具有next属性
let o = iter.next();
while (!o.done) {
console.log(o);
o = iter.next();
}
/*
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 4, done: false }
*/
console.log(o); // { value: undefined, done: true }
自己随便实现一个迭代器,迭代对象里面的集合
const o = {
list: [1, 2, 3]
};
o[Symbol.iterator] = function () {
let index = 0;
let _this = this;
return {
next: function () {
let value = _this.list[index];
if (index < _this.list.length) {
index++;
return { done: false, value };
}
return { done: true };
}
};
};
for (let v of o) {
console.log(v); // 1 2 3
}
给链表实现一个迭代器
function Node(data) {
this.data = data;
this.next = null;
}
Node.prototype[Symbol.iterator] = function () {
let cur = this;
return {
next: function () {
if (cur) {
const data = cur.data;
cur = cur.next;
return {
value: data,
done: false
};
}
return { done: true };
}
};
};
const p1 = new Node(1);
const p2 = new Node(2);
p1.next = p2;
const p3 = new Node(3);
p2.next = p3;
for (let nodeValue of p1) {
console.log(nodeValue);
}
给类数组(ArrayLike) 部署迭代器
const arrLike = {
0: 0,
1: 1,
2: 2,
length: 3
};
arrLike[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (let v of arrLike) {
console.log(v); // 0 1 2
}