1.定义
Map:对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值)都可以作为键或值。
weakMap: 是一种键值对的集合,其中的键必须是对象或非全局注册的符号,且值可以是任意的 JavaScript类型,并且不会创建对它的键的强引用。
Set:对象允许你存储任何类型(无论是原始值还是对象引用)的唯一值。
weakSet:是可被垃圾回收的值的集合,包括对象和非全局注册的符号。
2.Map和Set的使用
Map和Set的初始化都是通过new对应的构造函数来创建的。
Map是通过键-值(key-value)的形式进行新数据的储存。类对象
Set则是通过值(value)的形式进行新数据的储存。类数组
js
let map = new Map()
let set = new Set()
// Map通过set方法存储新值
map.set('name', 'alice')
// Set则是通过add方法新增新值
set.add(2)
console.log(map, set)//Map(1) { 'name' => 'alice' } Set(1) { 2 }
// 需要注意的是,map和set都不可以通过赋值操作来追加新的储存值
// 尽管你可以通过赋值来创建对象,但是这并不能作为一个Map、Set认可的新值
// 无法使用对应的实例方法获得对应的值的存在
map['name'] = 'Alice'
map['age'] = 18
console.log(map.has('age'))//false
set[0] = [1, 2]
console.log(set.has('age'))//false
console.log(map, set)//Map(0) { name: 'Alice', age: 18 } Set(0) { '1': [ 1, 2 ] }
const map = new Map([['name', 'Alice'], ['age', 18]])
const set = new Set([1, 2, 3, 4, { 'name': 'Bob' }])
console.log(map, set);//Map(2) { 'name' => 'Alice', 'age' => 18 } Set(5) { 1, 2, 3, 4, { name: 'Bob' } }
// Map和Set的常见属性和方法用法
// 1.has
// has用于查找Map和Set结构中有没有对应的键,如果有则返回true反之没有返回false
map.has('name')//true
const obj = { name: 'Bob' }
set.add(obj)
set.has({ name: 'Bob' })//false
set.has(obj)//true
// 为什么在Set中都存在,但是前者false,后者true?
// 这是因为字面量无法准确表达一个确定的索引,当使用变量去代替,set就会认为这个是相同的值,从而返回false
// 2.get
// get用于查找map中对应键的值
console.log(map.get('name')); //Alice
// 查找不存的键返回undefined
console.log(map.get('name1')); //undefined
// 3.delete
// delete表示删除对应Map的键-值以及Set中的值
console.log(map.delete('name'));//true
console.log(set.delete(2));//true
console.log(map, set);//Map(1) { 'age' => 18 } Set(4) { 1, 3, 4, { name: 'Bob' } }
// 删除不存在的值就会返回false,实际上就是对应键或值有没有被成功的删除
console.log(map.delete('name'));//false
console.log(set.delete(2));//false
// 4.keys和values
// 在map字典中,keys用于获取对应map的所有键值形成的数组,values则是用于获取所有值形成的数组
// 在set集合中,keys和values被认为等价,返回对应set的集合中的所有值返回的数组
console.log(map.keys(), map.values())//[Map Iterator] { 'name', 'age' } [Map Iterator] { 'Alice', 18 }
console.log(set.keys(), set.values())//[Set Iterator] { 1, 2, 3, 4, { name: 'Bob' } } [Set Iterator] { 1, 2, 3, 4, { name: 'Bob' } }
// 上述形成的可迭代数组都是可以继续被遍历,但是需要注意先转化为数组,在进行遍历
Array.from(map.keys()).forEach((item) => {
console.log(item);//获取map字典中每一项的键
})
Array.from(set.keys()).forEach((item) => {
console.log(item);//获取set集合中每一项的值
})
// 5.clear和size
// 用于清空map字典或set集合
// size表示对应目标map或set中存在的有效项个数
console.log(map.size)//2
console.log(set.size)//5
map['location'] = '上海'
console.log(map.size)//2
// 上述之所以任然是2,就是因为这种添加项的方式并不被认可为有效新增
map.clear()
set.clear()
console.log(map)//Map(0) {}
console.log(set)//Set(0) {}
// 6.entires
// 用于获取所有存在的键值对
// set由于没有Map中类似的key,为了保持API类似,set中的entries其实都是一样的[value,value]
console.log(map.entries())//[Map Entries] { [ 'name', 'Alice' ], [ 'age', 18 ] }
console.log(set.entries())
// [Set Entries] {
// [ 1, 1 ],
// [ 2, 2 ],
// [ 3, 3 ],
// [ 4, 4 ],
// [ { name: 'Bob' }, { name: 'Bob' } ]
// }
3.weakMap和weakSet
由于weakMap和weakSet之间数据的连接都是弱引用的。因此他们的示例方法只有基本的增删改查。
也就是
weakMap只能使用get、has、set、delete方法。
weakSet只能使用add、has、delete方法
weakMap和Map的区别:
- 不会阻止垃圾回收,直到垃圾回收器移除了键对象的引用
- 任何值都可以被垃圾回收,只要它们的键对象没有被
WeakMap
以外的地方引用
weakSet和Set的区别:
WeakSet
只能是对象和符号 的集合,它不能像 Set 那样包含任何类型的任意值。WeakSet
持弱引用 :WeakSet
中对象的引用为弱 引用。如果没有其他的对WeakSet
中对象的引用存在,那么这些对象会被垃圾回收。
4.Object和Map以及Set和数组之间的区别和使用场景
特性 | Object | Map | Array | Set |
---|---|---|---|---|
键类型 | String/Symbol | 任意类型 | 数字索引 | 无键,只有值 |
顺序 | ES6+ 有顺序 | 插入顺序 | 索引顺序 | 插入顺序 |
迭代 | 需要转换 | 直接可迭代 | 直接可迭代 | 直接可迭代 |
大小 | 手动计算 | .size 属性 | .length 属性 | .size 属性 |
性能 | 一般 | 增删改查快 | 索引访问快 | 值存在性检查快 |
4.1 Object和Map
Map的优势:
js
// 1. 键可以是任意类型
const map = new Map();
map.set({}, '对象作为键');
map.set(123, '数字作为键');
map.set(null, 'null作为键');
// 2. 保持插入顺序
const map = new Map();
map.set('z', 1);
map.set('a', 2);
console.log([...map.keys()]); // ['z', 'a'] - 保持插入顺序
// 3. 容易获取大小
console.log(map.size); // 直接获取
// 4. 更好的性能(大量增删操作)
object的优势:
js
// 1. 字面量语法更简洁
const obj = { key: 'value' };
// 2. JSON 序列化/反序列化
const json = JSON.stringify(obj);
const parsed = JSON.parse(json);
// JSON.stringify()和JSON.parse()不能用于Map字典的序列化和反序列化,会被认为是无效值
console.log(JSON.stringify(map))//{}
// 3. 方法支持
obj.hasOwnProperty('key'); // Map 需要用 has()
4.2 Set和数组
Set的优势:
js
// 1. 自动去重
const set = new Set([1, 2, 2, 3, 3]);
console.log([...set]); // [1, 2, 3]
// 2. 值存在性检查更快 O(1)
const set = new Set([1, 2, 3]);
console.log(set.has(2)); // true - 非常快
// 3. 保持插入顺序
数组的优势:
js
// 1. 有索引,可以按位置访问
const arr = [1, 2, 3];
console.log(arr[1]); // 2
// 2. 丰富的内置方法
arr.push(4); // 追加
arr.pop(); // 弹出
arr.slice(1,2); // 切片
arr.sort(); // 排序
// 3. 允许重复值
const arr = [1, 1, 2, 2]; // 允许重复
4.3 Map/Set/Object/Array的使用场景
Map使用场景
js
// 1. 需要复杂键时
const userPermissions = new Map();
const user = { id: 1, name: 'Alice' };
userPermissions.set(user, ['read', 'write']);
// 2. 需要保持插入顺序时
const operationLog = new Map();
operationLog.set('create', Date.now());
operationLog.set('update', Date.now());
// 3. 频繁增删键值对时
Set使用场景
js
// 1. 需要存储唯一值时
const uniqueTags = new Set(['js', 'css', 'js', 'html']);
// 结果: Set(3) {'js', 'css', 'html'}
// 2. 快速存在性检查时
const bannedIPs = new Set(['192.168.1.1', '10.0.0.1']);
if (bannedIPs.has(currentIP)) {
// 拒绝访问
}
// 3. 集合运算时
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);
const union = new Set([...setA, ...setB]); // 并集
Object的使用场景
js
// 1. 简单的键值对存储
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3
};
// 2. 需要 JSON 序列化时
localStorage.setItem('settings', JSON.stringify(config));
// 3. 需要使用方法时
if (config.hasOwnProperty('apiUrl')) {
// ...
}
Array的使用场景
js
// 1. 需要有序列表时
const todoList = ['买菜', '做饭', '洗碗'];
// 2. 需要按索引访问时
console.log(todoList[0]); // '买菜'
// 3. 需要重复值时
const scores = [95, 95, 88, 88]; // 允许重复分数
// 4. 需要丰富的数据操作时
const completed = todoList.filter(task => task.done);
const sorted = todoList.sort();
- 用 Map:当需要任意类型的键、保持顺序、频繁增删时
- 用 Object:当键都是字符串、需要JSON序列化、简单配置时
- 用 Set:当需要存储唯一值、快速存在性检查时
- 用 Array:当需要有序列表、索引访问、丰富操作方法时
5.性能对比
操作 | Map | Object | Set | Array |
---|---|---|---|---|
查找 | O(1) | O(1) | O(1) | O(n) |
插入 | O(1) | O(1) | O(1) | O(1) |
删除 | O(1) | O(1) | O(1) | O(n) |
迭代 | 快 | 慢 | 快 | 快 |
6.弱引用和强引用的含义:在于会不会被GC认为是存在引用链接。
强引用会阻止对象被垃圾回收器回收。只要强引用存在,对象就会一直保留在内存中。
弱引用不会阻止对象被垃圾回收器回收。如果对象只有弱引用指向它,垃圾回收器可以回收这个对象。