Set
和 Map
区别:
元素存储方式
Set
:存储唯一的值,每个值只能出现一次,类似于一个值的集合。
Map
:存储键值对,键和值可以是任何类型的值(包括对象)。
迭代方式
Set
:可以通过for...of
循环直接迭代其值。
Map
:可以通过for...of
循环迭代其键值对。
方法
Set
:具有add
(添加元素)、delete
(删除元素)、has
(检查是否存在元素)等方法。
Map
:具有set
(设置键值对)、get
(获取值)、delete
(删除键值对)、has
(检查是否存在键)等方法。
大小获取
Set
:使用size
属性获取元素数量。
Map
:使用size
属性获取键值对数量。
键的比较
Set
:判断元素是否重复是基于值的严格相等(===
)。
Map
:键的比较基于Object.is
,更准确地处理特殊值,如NaN
。
以下是示例代码展示它们的用法:
// Set 示例
let mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(2); // 重复添加,不会有效果
console.log(mySet.size);
mySet.delete(1);
console.log(mySet.has(2));
// Map 示例
let myMap = new Map();
myMap.set('key1', 10);
myMap.set('key2', 20);
console.log(myMap.size);
console.log(myMap.get('key1'));
myMap.delete('key1');
console.log(myMap.has('key2'));
总之,Set
适合存储不重复的值集合,而 Map
适合存储键值对形式的数据。
Set
使用场景:
去重操作
当需要从一个数组或其他可迭代对象中去除重复的元素时,可以将其转换为
Set
,然后再转换回数组。
集合运算
可以方便地进行集合的并集、交集、差集等运算。
快速查找
检查一个元素是否在集合中存在的时间复杂度为 O(1),效率很高。
存储唯一的对象引用
例如,在处理一组具有唯一标识的对象时,可以使用
Set
来确保不会有重复的对象。
事件处理
存储已注册的事件处理函数,确保不会重复注册相同的函数。
缓存数据
用于缓存一些唯一的数据,以提高查找和操作的效率。
以下是一些示例代码展示上述场景的应用:
// 去重
let array = [1, 2, 2, 3, 3, 3];
let uniqueArray = Array.from(new Set(array));
// 集合运算
let setA = new Set([1, 2, 3]);
let setB = new Set([2, 3, 4]);
// 并集
let union = new Set([...setA,...setB]);
// 交集
let intersection = new Set([...setA].filter(x => setB.has(x)));
// 差集
let difference = new Set([...setA].filter(x =>!setB.has(x)));
// 快速查找
let mySet = new Set([10, 20, 30]);
console.log(mySet.has(20));
// 存储唯一的对象引用
let obj1 = { id: 1 };
let obj2 = { id: 2 };
let setOfObjects = new Set([obj1, obj2]);
// 事件处理
let eventHandlers = new Set();
function registerHandler(handler) {
eventHandlers.add(handler);
}
Map
使用场景:
键值对数据存储
当需要以非字符串类型(如对象、函数等)作为键来存储和获取相关值时,
Map
比使用对象作为键值对存储更合适,因为对象的键只能是字符串或符号。
缓存数据
可以将计算结果或从外部获取的数据以特定的键进行缓存,以避免重复计算或请求。
关联数据
例如,将用户 ID 与用户详细信息关联起来,通过用户 ID 快速获取用户信息。
数据转换
对一组数据进行基于键的转换和处理。
维护状态
在一些复杂的应用中,
Map
可以用于维护特定的状态信息,根据不同的键来跟踪和更新相关状态。
以下是一些示例代码展示上述场景的应用:
// 键值对数据存储(使用对象作为键)
let key = { name: 'John' };
let myMap = new Map();
myMap.set(key, 25);
console.log(myMap.get(key));
// 缓存数据
let cacheMap = new Map();
function expensiveComputation(key) {
if (!cacheMap.has(key)) {
let result = // 复杂计算
cacheMap.set(key, result);
}
return cacheMap.get(key);
}
// 关联数据
let userMap = new Map();
userMap.set(1, { name: 'Alice', age: 20 });
userMap.set(2, { name: 'Bob', age: 25 });
console.log(userMap.get(1));
// 数据转换
let data = [
{ id: 1, value: 10 },
{ id: 2, value: 20 },
{ id: 3, value: 30 }
];
let transformedMap = new Map();
data.forEach(item => transformedMap.set(item.id, item.value * 2));
// 维护状态
let stateMap = new Map();
stateMap.set('isLoading', false);
stateMap.set('errorMessage', '');
map 与 Object 的区别
键的类型
Map
的键可以是任何类型的值,包括对象、函数、基本数据类型等。
Object
的键通常被限制为字符串或符号(在 ES6 中引入)。
键的顺序
Map
会保留键值对的插入顺序。
Object
的属性顺序是不确定的,特别是在不同的浏览器和 JavaScript 引擎中可能不同。
键的唯一性
在
Map
中,键是唯一的。如果尝试设置相同的键两次,新的值将覆盖旧的值。
Object
中,如果使用相同的字符串作为属性名多次,后面的属性值会覆盖前面的。
尺寸获取
Map
可以通过size
属性直接获取元素数量。
Object
需要手动计算属性数量。
迭代方式
Map
提供了forEach
方法以及entries
、keys
、values
等迭代器方法,更方便进行迭代操作。
Object
需要通过for...in
循环来遍历属性,但这可能会遍历到继承的属性。
原生支持方法
Map
具有一些特定的方法,如set
、get
、delete
、has
等,用于操作键值对。
Object
则通过属性访问操作符(.
和[]
)来进行类似的操作。
以下是示例代码展示它们的区别:
// Map
let myMap = new Map();
myMap.set(1, 'One');
myMap.set('two', 2);
myMap.set({ key: 'value' }, 'Object as key');
console.log(myMap.size);
// Object
let myObject = {};
myObject[1] = 'One';
myObject['two'] = 2;
myObject[{ key: 'value' }] = 'Object as key';
// 计算对象属性数量的函数
function countProperties(obj) {
return Object.keys(obj).length;
}
console.log(countProperties(myObject));