# 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...offorEach

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方法,都会返回一个代表当前成员的信息对象,具有valuedone两个属性。

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
}

上次更新: 1/22/2025, 9:39:13 AM