我们经常需要做键值对存储,大多时候我们会选择对象字面量Object ({}) 来存储,往往会忽略ES6 新提供的数据结构:Map。从字面上来看,Map才是正确的选择。

接下来我们一起来比较一下对象字面量 和 Map 的差别。

ES6 键值对存储:Object 还是 Map?_java


基本使用

添加键值对,访问键值操作。

Object

// init
const keyValue = {}
// add
keyValue['firstname'] = 'tony'
// get
keyValue['firstname']

Map

// init
const keyValue = new Map()
// add
keyValue.set('firstname', 'tony')
// get
keyValue.get('firstname')

当访问对象未定义的属性或键时,都会访问undefined

const o = {}
o['firstname'] // undefined
const m = new Map()
m.get('firstanme') // undefined

迭代

键值对也经常需要进行遍历操作。Map 数据类型本身是可迭代的,内置forEach方法

const kv = new Map()
kv.set('firstname', 'tony')
kv.set('lastname', 'stark')
kv.forEach((value, key) => console.log(key, value))
// firstname tony
// lastname stark

Object没有forEach方法,你可以使用 for … in :

{id: 1, name: "test"}
for (var key in obj){
console.log(`key: ${key}, value: ${obj[key]}`);
//key: id, value: 1
//key: name, value: test
}

也可以通过内置的迭代器:entries(), values(), keys() 生成可迭代的数据。

const kv = {}
kv['firstname'] = 'tony'
kv['lastname'] = 'stark'
Object.keys(kv).forEach((key) => console.log(key, kv[key]))
// firstname tony
// lastname stark

或者

Object.entries(kv).forEach((entry) => console.log(entry[0], entry[1]))
// firstname tony
// lastname stark

相比之下,Map 的迭代操作更简单,可读性更好一些。


检查是否存储某键值

const kv = new Map()
kv.set('firstname', 'tony')
kv.set('lastname', 'stark')
kv.has('firstname') // true
kv.has('nickname') // false

以上 Map 操作很简单,以下 Object 相对复杂:

const kv = {}
kv['firstname'] = 'tony'
kv['lastname'] = 'stark'
Object.prototype.hasOwnProperty.call(kv, 'firstname') // true
Object.prototype.hasOwnProperty.call(kv, 'nickname') // false

查看键值对数量

Map有内置的size属性

const kv = new Map()
kv.set('firstname', 'tony')
kv.set('lastname', 'stark')
kv.size // 2

Object 需要先生成一个可迭代集合。

const kv = {}
kv['firstname'] = 'tony'
kv['lastname'] = 'stark'
Object.keys(kv).length // 2

其它细节

1 Object 对象字面量的键只能是string类型,但Map的键可以是任何类型。

const m = new Map()
m.set(['a', 'b'], true)
m.set(['a', 'c'], false)

但Object 对象字面量,如果输入一个数值,也会转化成字符串。

const o = {}
o[1] = 'iron man'
o[1] // 'iron man'
o['1'] // 'iron man'

2 Object对象拥有原型prototype,意味着它已内置一些键值,增加复杂度。

const o = {}
o['constructor'] // ƒ Object() { [native code] }
const m = new Map()
m.get('constructor') // undefined

3 JSON能够序列化Object ,无法序列化Map。

const kv = {}
kv['firstname'] = 'tony'
kv['lastname'] = 'stark'
JSON.stringify(kv) // '{"firstname":"tony","lastname":"stark"}'

而Map不行

const kv = new Map()
kv.set('firstname', 'tony')
kv.set('lastname', 'stark')
JSON.stringify(kv) // '{}'

所以涉及到转化成json进行http传输时,Map不是好的选择。


Object vs Map 你到底选谁?

在我看来,只要你不需要序列化数据,任何键值对都应该用Map 来存储。

Map可以不局限于string,可以存储任意类型键值。同时有has()  size等方法的方法属性。

Object 适用于复杂的对象,比如具有一定的函数方法:

const ironman = {
       firstname: 'tony',
       lastname: 'stark',
       hello: function() {
              return `I am ${this.firstname} ${this.lastname}`
        }
}