四步带你深入Map、Set数据结构

24 阅读6分钟

blog-post-javascript-map-and-set-typed-collections.jpg

在前端开发的多彩世界里,数据结构是我们构建应用的基石。四种看似普通的结构——Map、Set、对象和数组——实则蕴藏着强大的力量。它们不仅支撑着我们日常的编码任务,更在性能优化和代码可读性上扮演着关键角色

为什么引入 Map 和 Set

  • 填补现有数据结构的空缺:在 ES6 之前,JavaScript 缺乏内置的键值对集合数据结构,Map 和 Set 的引入填补了这一空缺。

  • 提高性能:对于特定的操作,Map 和 Set 提供了比传统对象和数组更优的性能

  • 增强语言特性:Map 和 Set 的引入增强了 JavaScript 作为一门编程语言的特性,使其更加现代化和功能丰富, 更清晰的 API

  • 更好的数据结构支持:随着 Web 开发需求的增加,开发者需要更高效的数据结构来处理复杂的数据,Map 允许任何类型的键,而 Set 自动去重, Map 和 Set 提供了这样的支持,

API 介绍

Map

Map 是一种集合类型,它存储键值对的集合,其中键和值可以是任何类型。Map 对象保存插入顺序的元素。

Map 的基本 API

  • new Map():创建一个新的 Map 对象。
  • map.set(key, value):将一个键值对添加到 Map 中。
  • map.get(key):返回 Map 中与指定键关联的值。
  • map.has(key):检查 Map 中是否存在指定的键。
  • map.delete(key):从 Map 中删除指定的键。
  • map.clear():清除 Map 中的所有键值对。
  • map.size:返回 Map 中元素的数量。
  • map.keys():返回一个迭代器,包含 Map 中的所有键。
  • map.values():返回一个迭代器,包含 Map 中的所有值。
  • map.entries():返回一个迭代器,包含 Map 中的所有键值对。

Map 示例代码

创建 map 对象

let map = new Map();

设置值

map.set("key1", "value1");
map.set("key2", "value2");

获取值

console.log(map.get("key1")); // 输出:value1

遍历全部

for (let [key, value] of map) {
  console.log(`${key}: ${value}`);
}

查询值

console.log(map.has("key2")); // 输出:true
console.log(map.size); // 输出:1

删除值

map.delete("key1");

map.clear();

Set

Set 是一种集合类型,它只存储唯一的值。

Set 的基本 API

  • new Set():创建一个新的 Set 对象。
  • set.add(value):添加一个元素到 Set 中。
  • set.delete(value):从 Set 中删除指定的元素。
  • set.has(value):检查 Set 中是否存在指定的元素。
  • set.clear():清除 Set 中的所有元素。
  • set.size:返回 Set 中元素的数量。

Set 示例代码

创建 set 对象

let set = new Set();

设置值

set.add(1);
set.add(2);

获取值

for (let value of set) {
  console.log(value);
}

查询值

console.log(set.has(1)); // 输出:true
console.log(set.size); // 输出:2

删除值

set.delete(1);
set.clear();

对象

在 JavaScript 中,对象是键值对的集合,键是字符串(或符号),值可以是任意类型。

对象的基本 API

  • obj[key]:访问或设置对象的属性。
  • delete obj[key]:删除对象的属性。
  • Object.keys(obj):返回一个包含对象所有自身属性的数组。
  • Object.values(obj):返回一个包含对象所有自身属性值的数组。
  • Object.entries(obj):返回一个给定对象自身可枚举属性的键值对数组。

对象示例代码

// 创建方式1
let obj = { a: 1, b: 2 };
// 创建方式2
let obj_null = Object.create(null);
// 获取值
console.log(obj.a); // 输出:1
// 赋值
obj.c = 3;

// 获取keys,values
console.log(Object.keys(obj)); // 输出:['a', 'b', 'c']
console.log(Object.values(obj)); // 输出:[1, 2, 3]
// 遍历
for (let key in obj) {
  console.log(`${key}: ${obj[key]}`);
}

数组

数组是有序的数据项集合。

数组的基本 API

  • array[index]:访问或设置数组的元素。
  • array.length:获取或设置数组的长度。
  • array.push(element):向数组末尾添加一个或多个元素。
  • array.pop():移除数组的最后一个元素并返回该元素。
  • array.shift():移除数组的第一个元素并返回该元素。
  • array.unshift(element):在数组的开头添加一个或多个元素。
  • array.slice(start, end):返回一个新的数组,包含从 start 到 end(不包括 end)的原数组元素。
  • array.includes(element):判断数组是否包含指定的元素。

数组示例代码

let array = [1, 2, 3];

console.log(array[0]); // 输出:1
array.push(4);
console.log(array.length); // 输出:4

// pop 从尾部移除、 shift 从数组前面移除
array.pop(); // 移除4
// 检查
console.log(array.includes(2)); // 输出:true
// 遍历
for (let i = 0; i < array.length; i++) {
  console.log(array[i]);
}

API 转换

Map 转对象

let mapToObject = Object.fromEntries(map);

对象转 Map

let objectToMap = new Map(Object.entries(obj));

Set 转数组

let setToArray = [...set];

数组转 Set

let arrayToSet = new Set(array);

for...of 与 for...in

for..of

for...of 循环是一种用于遍历可迭代对象(iterable)的 JavaScript 语法。它允许你遍历数组、字符串、Map、Set以及任何实现了[Symbol.iterator]属性的对象。

可迭代对象

可迭代对象是具有默认迭代器的对象。迭代器是一个具有 next()方法的对象,该方法返回包含两个属性的对象:value 和 done。value 是迭代器当前返回的值,done 是一个布尔值,表示迭代器是否已经遍历完所有元素。

for...of 循环的原理

  • 获取迭代器:for...of 循环首先调用可迭代对象的Symbol.iterator方法,获取一个迭代器。
  • 执行迭代:然后,循环使用迭代器的 next()方法来获取下一个值。
  • 循环继续条件:for...of 循环会检查 next()方法返回的 done 属性。如果 done 是 false则循环继续,并将 value 赋值给循环变量。
  • 循环结束条件:当 done 属性为 true 时,循环结束。
  • 循环体执行:在每次迭代中,循环体中的代码会被执行,使用当前的 value。

让对象拥有 for...of 能力

let obj = {
  a: "001",
  b: "002",
  c: "003",
  [Symbol.iterator]() {
    let index = 0;
    const items = Object.keys(obj);
    return {
      next: () => {
        if (index < items.length) {
          return { value: items[index++], done: false };
        } else {
          return { done: true };
        }
      },
    };
  },
};

for (let item of obj) {
  console.log(item); // 依次输出 a,b,c
}

for...in

for...in 循环用于遍历对象的属性名(包括可枚举的自有属性和继承的属性)。

提一下Object.keys() 获取到的 keys 不包含原型链中的 key,这是 Object.keys 遍历for...in区别

特点:

  • 遍历对象的键(属性名)。
  • 不保证遍历的顺序。
  • 通常与 hasOwnProperty() 方法结合使用,以避免遍历原型链上的属性。

示例

let obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(key + ": " + obj[key]); // 输出:a: 1, b: 2, c: 3
  }
}

结语

经过对 Map、Set、对象和数组的深入剖析,我们不仅理解了它们各自的特性和用途,还学会了如何根据实际需求选择合适的数据结构。从填补 JavaScript 早期的空缺到成为现代 Web 开发中不可或缺的工具,Map 和 Set 的出现极大地丰富了我们处理数据的能力。同时,我们也认识到了对象和数组在 JavaScript 中的经典地位以及它们在特定场景下的不可替代性。

通过本文的 API 介绍和示例代码,掌握了如何有效地使用这些数据结构,并了解了它们之间的转换方法。无论是在性能优化、代码清晰度还是功能需求上,我们都有了一个更加明确的选择方向。能够更加自信地在前端开发中运用这些数据结构,打造出更加高效、优雅的代码。