Object
Object 是最常用的一种引用类型数据, 用于存储键值对的集合, 在 ECMAScript 1st 添加的
Map
Map 是键值对集合, 采用 Hash 结构存储, 在 ECMAScript 2015 版本里添加
为什么新增 Map 结构
共同点
键值对的动态集合, 支持增加和删除键值对
// Object 定义
const obj = {
a: 1,
b: 2
};
// 添加
obj.c = 3;
// 删除
delete obj.c;
// Map 定义
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.delete('a');
不同点
1. 构造方式
- Object
// 1. 对象字面量
const obj = {
'a': '1',
'b': 2
};
// 2. 构造函数
const obj1 = new Object();
// 3. Object.create()
const obj2 = Object.create();
- Map
// 1. 构造函数
const m1 = new Map();
const m2 = new Map([
['a', '1'],
['b', '2']
]);
console.log(m2);
2. 键的类型
- Object
键类型必须是 String 或 Symbol, 如果时非 String, 会进行数据类型转换
// arr1 数组作为键, 会转换为字符串
const obj2 = {
a: 1,
};
const arr1 = [1, 2];
obj2[arr1] = 'arr';
console.log(obj2); //{ a: 1, '1,2': 'arr' }
// 3 会被覆盖
const obj = {};
obj[3] = 3;
obj['3'] = 33;
console.log(obj); // {'3': 33}
- Map
可以时任意类型, 包括对象、数组、函数等, 不会进行类型转换。
在添加键值对时, 会通过严格相等(===) 来判断键属性是否已经存在。
const map2 = new Map();
const arr1 = [1, 2];
map2.set('a', 1);
map2.set('2', 2);
map2.set(2, 2);
map2.set(arr1, 'arr');
console.log(map2); // Map(4) { 'a' => 1, '2' => 2, 2 => 2, [ 1, 2 ] => 'arr' }
特例:
// NaN === NaN => false
const m = new Map();
m.set(NaN, 1);
m.set(NaN, 2);
console.log(m); // Map(1) { NaN => 2 }
3. 键的顺序
- Object
key 是无序的, 不会按照添加的顺序返回
- 对于大于等于 0 的整数, 会按照大小及逆行排序, 对于小数和负数会当作字符串处理
- 对于 string 类型, 按照定义的顺序输出
- 对于 Symbol 类型, 会直接过滤掉, 不会进行输出, 如果想要输出 Symbol 类型属性, 通过 Object.getOwnPropertySymbols() 方法
- Map
key 是有序的, 按照插入的顺序返回
const m = new Map();
m.set(2, 2);
m.set('1', 1);
m.set('b', 'b');
m.set(0, 0);
m.set(Symbol('s1', 's1'));
for (let key of m.keys()) {
console.log(key);
}
/*
2
1
b
0
Symbol(s1)
*/
4. 键值对
- Object
只能手动计算, 通过 Object.keys() 方法或者通过 for…in 循环统计
const obj = {
2: 2,
'1': 1,
'b': b
};
Object.keys(obj).length // 3
- Map
直接通过 size 属性访问
const m = new Map();
m.set(1, 1);
m.set(2, 2);
m.set(3, 3);
console.log(m.size); // 3
5. 键值对访问
- Object
- 添加或修改属性, 通过. 或者中括号形式
const obj = {};
obj.name = "zhangsan";
obj[Symbol('s5')] = 's5';
- 判断属性是否存在
obj.name === undefined
obj['name'] === undefined
- 删除属性使用 delete 关键字
delete obj.name
- Map
- 添加和修改 key-value
// 不存在的添加, 存在的修改
map.set(1, 1);
- 判断属性是否存在
m.has(1);
- 其它操作
// 获取
m.get(1);
// 删除
m.delete(1);
// 获取所有键
m.keys();
// 清空 map
m.clear();
console.log(m.keys()); // [Map Iterator] { 1, 2 }, 返回的是 map 迭代器
6. 迭代器- for…of
for...of
是 ES6新增的迭代具有 iterator
特性的机构
- Object
Object 本身不具有 iterator 特性, 默认情况下不能使用 for…of 进行遍历
- Map
Map 结构的 keys(), values(), entries() 方法返回值都具有 iterator 特性
const m = new Map();
m.set(1, 1);
m.set(2, 2);
for (let [k, v] of m.entries()) {
console.log(k, v);
}
/*
1 1
2 2
*/
7. JSON 序列化
- Object
可以通过 JSON.stringify() 进行序列化操作
const obj = {
1: 1,
2: 2
};
console.log(JSON.stringify(obj)); // {"1":1,"2":2}
- Map
不能直接进行 JSON 序列化操作
const m = new Map();
m.set(1, 1);
m.set(2, 2);
console.log(JSON.stringify(m)); //{}
console.log(JSON.stringify(Array.from(m))); // [[1,1],[2,2]]
使用场景
Object
- 仅做数据存储, 并且属性仅为字符串或者 Symbol
- 需要进行序列化转换为 json 传输
- 当做一个对象的实例, 需要保留自己的属性和方法
Map
- 会频繁更新和删除键值对
- 存储大量数据时, 尤其是 key 类型未知的情况下
- 需要频繁进行迭代处理