Object和Map的区别

500 阅读3分钟

概述

  • Object 是最常见的一种引用类型数据,可用于存储键值对的集合。
  • Map 是键值对的集合,采用Hash结构存储,ES6新增。

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 **/
// 对象字面量
const obj = {
    'a': 1,
    'b': 2
}
// 构造方法
const o = new Object();
const o2 = Object.create();

/** Map **/
// 构造方法
const m = new Map();
cosnt m2 = new Map([
    ['a', 1],
    ['b', 2]
]);
  1. 键的类型
/** object **/
键类型必须是String或者Symbol,如果是非String类型,会进行数据类型转换。
const obj2 = { a: 1};
const arr1 = [1, 2];
// 例1:
obj2[arr1] = 'arr';
console.log(obj2); => { a: 1, 1,2: 'arr'}
// 例2:
obj2[3] = 3;
obj2['3'] = 33;
console.log(obj2); => {3: 33, a: 1, 1,2: 'arr'}

/** Map **/
可以是任意类型,包括对象、数组、函数等,不会进行类型转换。在添加键值对时,会通过严格相等(===)来判断键属性是否已存在。
const map2 = new Map();
map2.set('a', 1);
map2.set('2', '2');
map2.set(2, 2);
map2.set(arr1, 1);
console.log(map2); => {'a' => 1, '2' => '2', 2 => 2, Array(2) => 1}
// 特例 NaN
console.log(NaN === NaN); => false
const map = new Map();
map.set(NaN, 1);
map.set(NaN, 2);
console.log(map); => {NaN => 2}
  1. 键的顺序
/** object **/
key是无序的,不会按照添加的顺序返回
1. 对于大于等于0的整数,会按照大小进行排序;对于小数和负数,会当做字符串处理。
2. 对于String类型,按照定义的顺序进行输出
3. 对于Symbol类型,会直接过滤掉,不会进行输出,如果想要输出Symbol类型属性,需要通过Object.getOwnPropertySymbol()方法
const obj3 = {
    2: 2,
    '1': 1,
    'b': 'b',
    1.1: 1.1,
    0: 0,
    'a': 'a',
    [Symbol('s1')]: 's1'
}
console.log(Object.keys(obj3)); => ['0''1''2''b''1.1''a']

/** Map **/
key是有序的,按照插入的顺序进行返回
const map3 = new Map();
map3.set(2, 2);
map3.set('1', 1);
map3.set('b', 'b');
map3.set(1.1, 1.1);
map3.set(0, 0);
map3.set('a', 'a');
map3.set(Symbol('s1'), 's1');
for(let key of map3.keys()) {
    console.log(key); => 2, '1', 'b', 1.1, 0, 'a', Symbol(s1)
}
  1. 键值对size
/** object **/
只能手动计算,通过Object.keys()方法或者通过for...in循环统计
const obj4 = {
    2: 2,
    '1': 1,
    'b': 'b'
}
console.log(Object.keys(obj4).length); => 3

/** Map **/
直接通过size属性访问
const map4 = new Map();
map4.set(2, 2);
map4.set('1', 1);
map4.set('b', 'b');
console.log(map4.size); => 3
  1. 键值对的访问
/** object **/
1. 添加或者修改属性,通过点或者中括号的形式
const obj5 = {};
obj5.name = 'sir';
obj5[Symbol('s5')] = 's5';
2. 判断属性是否存在
obj5.name === undefined;
obj5['name'] === undefined;
3. 删除属性,通过delete关键字
delete obj5.name;

/** Map **/
1. 添加和修改key-value
const map5 = new Map();
map5.set('name', 'sir');
map5.set(Symbol('s5'), 's5');
2. 判断属性是否存在
map5.has('name'); => true
map5.has('age'); => false
3. 取值
map5.get('name'); => 'sir'
4. 删除键值对
map5.delete('name');
5. 获取所有的属性名
map5.keys();
6. 清空map
map5.clear();
  1. 迭代器--for...of
/** object **/
Object本身不具有Iterator特性,默认情况下不能使用for...of进行遍历。
const obj6 = {
    name: 'sir',
    age: 28
}
for (let key of obj6) {} => Uncaught TypeError: obj6 is not iterable

/** Map **/
Map结构的keys(),values(),entries()方法返回值都具有Iterator特性
const map6 = new Map([
    ['name', 'sir'],
    ['age', 28]
]);
for(let [key, value] of map6.entries()) {
    console.log(key, value); => name sir, age 28
}
  1. JSON序列化
/** object **/
Object类型可以通过JSON.stringify()进行序列化操作
const obj7 = {
    name: 'sir',
    age: 28
}
JSON.stringify(obj7); => '{"name":"sir","age":28}'

/** Map **/
Map结构不能直接进行JSON序列化
const map7 = new Map([
    ['name', 'sir'],
    ['age', 28]
])
JSON.stringify(map7); => '{}'
如果想要将map执行序列化操作,需要将其转换为数组
JSON.stringify(Array.from(map7)); => '[["name","sir"],["age",28]]'
  1. 应用场景
/** object **/
1. 仅做数据存储,并且属性仅为字符串或者Symbol
2. 需要进行序列化转换json传输时
3. 当做一个对象的实例,需要保留自己的属性和方法时

/** Map **/
1. 会频繁更新和删除键值对时
2. 存储大量数据时,尤其是key类型未知的情况下
3. 需要频繁进行迭代处理