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 是无序的, 不会按照添加的顺序返回

  1. 对于大于等于 0 的整数, 会按照大小及逆行排序, 对于小数和负数会当作字符串处理
  2. 对于 string 类型, 按照定义的顺序输出
  3. 对于 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
  1. 添加或修改属性, 通过. 或者中括号形式
const obj = {};
obj.name = "zhangsan";
obj[Symbol('s5')] = 's5';
  1. 判断属性是否存在
obj.name === undefined
obj['name'] === undefined
  1. 删除属性使用 delete 关键字
delete obj.name
  • Map
  1. 添加和修改 key-value
// 不存在的添加, 存在的修改
map.set(1, 1);
  1. 判断属性是否存在
m.has(1);
  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 类型未知的情况下
  • 需要频繁进行迭代处理