一、简单了解一下这些数据结构
Map(字典)
Map 是一种新的集合类型,为 ECMAScript 语言带来了真正的键/值存储机制,Map 的大多数特性都可以通过 Obeject 类型实现,但二者之间还是存在一些细微的差异。
- 本质上是键值对的集合,类似集合
- 可以遍历,方法很多可以跟各种数据格式转换:set、get、has、delete、clear、keys、values、entries、forEach
WeakMap
WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意。
WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
- 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
- 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
- 不能遍历,方法有 get、set、has、delete
Set(集合)
它是 ES6 新增的一种数据结构,类似于数组,但成员是唯一且无序的,没有重复的值。
它是一个 构造函数,用来生成 Set 数据结构
- 成员唯一、无序
- [value, value],键值与键名是一致的(或者说只有键值,没有键名)
- 可以遍历,方法有:add、delete、has、clear、keys、values、entries、forEach
WeakSet
WeakSet 对象允许你将 弱引用对象 储存在一个集合中
- 成员唯一、都是对象
- 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
- 不能遍历,方法有 add、delete、has
Object(对象)
引用类型指的是 object
object 包括内置对象、宿主对象、自定义对象
内置对象中有 Object、Function、Array、String、Number、Boolean 等原生对象构造函数
在 JavaScript 中,一切皆对象(除 undefined、null 外)
二、Map 和 WeakMap
Map
Map 是 ES6 中的一个数据结构,它类似于对象,但它的 key 可以是任何类型,不仅限于字符串或 Symbol 类型。Map 的使用场景包括需要对一组键值对进行处理时,或者需要根据键查找值时。
使用示例:
let demo1 = new Map()
实例对象的属性:
- size,返回 Map 实例包含的 键值对 总数
实例对象的方法:
- get(key):返回键所对应的值,如果键不存在则返回 undefined。
- set(key, value):设置键值对。
- has(key):返回一个布尔值,表示 WeakMap 中是否存在该键。
- delete(key):删除指定键的键值对。
- clear():清空 WeakMap 中的所有键值对。
// 创建一个空的
Mapconst myMap = new Map();
// 添加键值对
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
myMap.set('key3', 'value3');
// 获取指定键对应的值
console.log(myMap.get('key1')); // 输出:value1
// 检查是否存在指定的键
console.log(myMap.has('key4')); // 输出:false
// 删除指定键对应的键值对
myMap.delete('key2');
// 清空 Map 中所有的键值对
myMap.clear();
// 使用 entries() 遍历 Map 中的所有键值对
for (const [key, value] of myMap.entries()) {
console.log(`${key}: ${value}`);
}
WeakMap
WeakMap 对象是一种新的集合类型,它是一组 键值对 的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
WeakMap 是 Map 的兄弟类型,其 API 也是 Map 的子集。
弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,如果没有其他的变量或属性引用这个对象值,则这个对象将会被垃圾回收掉(不考虑该对象还存在于 WeakSet 中),所以,WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有没有运行,运行前后成员个数可能不一致,遍历结束之后,有的成员可能取不到了(被垃圾回收了)。
WeakMap 的使用方法和 Map 相似,它可以存储键值对,并且可以使用 get()、set()、has()、delete() 等方法进行操作。它的主要用途是作为一种存储对象私有数据的方式,因为 WeakMap 中存储的数据不会被视为对象的属性,因此不会影响垃圾回收。
WeakMap 有以下属性和方法:
- size:返回 WeakMap 中键值对的数量。
- get(key):返回键所对应的值,如果键不存在则返回 undefined。
- set(key, value):设置键值对。
- has(key):返回一个布尔值,表示 WeakMap 中是否存在该键。
- delete(key):删除指定键的键值对。
- clear():清空 WeakMap 中的所有键值对。
WeakMap 不同时间段的数据可能是不同的,
const wm = new WeakMap();
const obj = {};
wm.set(obj, "value");
console.log(wm.get(obj)); // "value"
wm.delete(obj);
console.log(wm.has(obj)); // false
三、Set 和 WeakSet
Set
Set 是 ES6 中新增的数据结构,用于存储一组唯一的值(不重复),类似于数组,但是成员的值都是唯一的,没有重复的值。
使用方法
let demo1 = new Set()
实例对象的属性:
- constructor:构造函数,默认为 Set 函数。
- size:返回 Set 实例的成员总数。
实例对象的方法:
- add(value):向 Set 对象添加一个新的元素,返回该 Set 对象。
- clear():清空 Set 对象所有的元素,没有返回值。
- delete(value):从 Set 对象中删除指定的元素,返回一个布尔值,表示删除是否成功。
- has(value):判断 Set 对象中是否存在指定的元素,返回一个布尔值。
使用场景
- 去重。可以通过 Set 来去除数组中的重复项,也可以将字符串中的重复字符去除。
- 交集、并集、差集等操作。可以通过 Set 的一些方法来实现集合运算,例如交集可以通过两个 Set 对象使用 filter() 方法实现。
- ES6 中 Map 和 WeakMap 的实现。Set 内部实现方式和 Map 类似,可以使用 Set 作为 Map 中的键值对的 Key,这样就可以实现 Map 的功能。同时,WeakSet 内部也使用了 Set 实现。
const mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(2); // 不会重复添加
console.log(mySet); // Set(3) {1, 2, 3}
console.log(mySet.size); // 3
console.log(mySet.has(2)); // true
mySet.delete(2);
console.log(mySet); // Set(2) {1, 3}
mySet.clear();
console.log(mySet); // Set(0) {}
使用 Set 进行数组去重
const arr2 = [1, 22, 22, 3, 4, 5, 5, 6]
console.log([...new Set(arr2)])
Set 中的特殊值
Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:
- +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复
- undefined 与 undefined 是恒等的,所以不重复
- NaN 与 NaN 是不恒等的,但是在 Set 中认为NaN与NaN相等,所有只能存在一个,不重复。
WeakSet
WeakSet 是 ES6 中新增的一种集合类型,它与 Set 类似,不同之处在于它只能存储对象,且只持有对象的弱引用。也就是说,当一个对象被 WeakSet 引用时,如果这个对象不再被其他变量或属性引用,则这个对象会被自动回收,而不需要手动将它从 WeakSet 中删除。
WeakSet 的使用方法和 Set 很相似,也有 add、has 和 delete 方法。不同的是,WeakSet 不支持 forEach 和 size 属性,也不能遍历其中的所有元素。因为 WeakSet 中的元素是对象的弱引用,所以不能保证它们的引用可用,也不能保证元素的顺序。
WeakSet 的主要应用场景是管理 DOM 节点的引用,当某个 DOM 节点被删除时,其在 WeakSet 中的引用也会自动被删除,从而避免了内存泄漏。
下面是 WeakSet 的常用属性和方法:
- WeakSet.prototype.add(value): 向 WeakSet 中添加一个对象。
- WeakSet.prototype.has(value): 判断 WeakSet 中是否存在某个对象。
- WeakSet.prototype.delete(value): 从 WeakSet 中删除某个对象。
下面是一个使用 WeakSet 的示例:
let ws = new WeakSet();
let obj1 = { a: 1 };
let obj2 = { b: 2 };
ws.add(obj1);
ws.add(obj2);
console.log(ws.has(obj1)); // true
console.log(ws.has(obj2)); // true
obj1 = null;
console.log(ws.has(obj1)); // false
console.log(ws.has(obj2)); // true
在这个示例中,我们创建了一个 WeakSet,并向其中添加了两个对象。之后我们将 obj1 设为 null,这样它就不再被任何变量引用了,于是它会被自动回收。这时候,我们再次调用 ws.has(obj1) 的结果为 false,说明 obj1 已经被删除了,而 obj2 还存在于 WeakSet 中。
四、Object(对象)
属性继承
JavaScript 对象可以从一个称为原型的对象里继承属性。对象的方法通常是继承的属性。这种”原型式继承“(prototypal inheritance)是 JavaScript 的核心特征。
使用方法
var obj = new Object(); // 效果如同 var obj = {}
实例对象的属性
- Object.prototype.constructor:一个引用值,指向 Object 构造函数
- Object.prototype.proto:指向一个对象,当一个 object 实例化时,使用该对象作为实例化对象的原型
实例对象的方法
- Object.assign():通过复制一个或多个对象来创建一个新的对象
- Object.create():使用指定的原型对象和属性创建一个新对象
- Object.defineProperty():给对象添加一个属性并指定该属性的配置
- Object.defineProperties():给对象添加多个属性并分别指定它们的配置
- Object.entries():返回给定对象自身可枚举属性的 [key, value] 数组
- Object.keys():返回一个包含所有给定对象自身可枚举属性名称的数组
- Object.values():返回给定对象自身可枚举值的数组
原型方法
- Object.prototype.hasOwnProperty():返回一个布尔值,用于表示一个对象自身是否包含指定的属性,该方法并不会查找原型链上继承来的属性用 hasOwnProperty 就能检测出,它能区别自身属性与继承属性
- Object.prototype.isPrototypeOf():返回一个布尔值,用于表示该方法所调用的对象是否在指定对象的原型链中
- Object.prototype.toString():返回一个代表该对象的字符串。
- Object.prototype.valueOf():返回指定对象的原始值
五、区别
Map 与 Set 的区别是什么?
共同点:集合、字典 都是储存不重复的值
不同点:
- Map 是键值对,Set 是不重复的值
- Map 可以通过 get 方法获取值,而 Set 不能因为它只有值
- Set 的值是唯一的可以用来做数组去重,Map 由于没有格式限制,可以做数据存储
Map 与 Object 的区别是什么?
Object:是最常用的一种引用类型数据,可用于存储键值对的集合,在 ECMAScript 1st 里添加的
Map:是键值对的集合,采用 Hash 结构存储,在 ECMAScript 2015 版本里新增的
相同点:键值对的动态集合,支持增加删除键值对
不同点概述:
- Object 键类型必须是 String 或者 Symbal(否则会进行类型转换);Map 键可以说任意类型
- Object key 是无序的,Map key 是有序的,按插入顺序返回
- Map 可以通过 size 属性直接访问键值对数量,Object 只能手动计算
- Object 添加或修改属性使用点或者中括号形式,Map 使用 set 方法插入键值对
不同点详细介绍:
- 键的类型
- Object:键类型必须是 String 或者 Symbol,如果非 String 类型,会进行数据类型转换
- Map:键可以是任意类型,包括对象,数组,函数等。不会进行数据类型转换。
- 键的顺序
- Object:key是无序的,不会按照添加到顺序返回
- Map:key 是有序的,按照插入的顺序返回
- 键值对 size
- Object:只能手动计算,通过 Object.keys() 方法或者通过 for…in 循环统计
- Map:直接通过 size 属性访问
- 键值对的访问
- Object:添加或者修改属性,通过点或者中括号的形式
- Map:添加和修改 Key-value
- 迭代器
- Object:不具有 迭代器-iterator 特性(Object 本身不具有 iterator 特性,默认情况下不能使用 for…of 进行遍历)
- Map:Map 结构的 keys(), values(), entries() 方法返回值都具有 iterator 特性
- JSON 序列化
- Object:Object 类型可以通过 JSON.stringify() 进行序列化操作
- Map:Map 结构不能直接进行 JSON 序列化
应用场景
Object
- 仅做数据存储,并且属性仅为字符串或者 Symbol 类型
- 需要进行序列化转换为 json 传输时
- 当做一个对象的实例,需要保留自己的属性和方法时
Map
- 会频繁更新和删除键值对时
- 存储大量数据时,尤其时 key 类型未知的情况下
- 需要频繁进行迭代处理
选择 Object 还是 Map
对于 web 开发任务来说,选择 Object 还是 Map 只是个人偏好问题,但对于在乎内存和性能的开发者来说,对象和映射之间确实存在显著区别。
- 内存占用:给定固定大小的内存,Map 可以比 Object 多存储 50% 的键值对。
- 插入性能:插入 Map 稍微快一点。
- 查找速度:Object 更优。
- 删除性能:Map 的 delete() 操作都比插入和查找更快。