JavaScript 中有了Object 为什么还需要 Map 呢

目录
  • 一、别把对象当 Map
    • 1、可能通过原型链访问到未定义的属性
    • 2、对象的 Key 只能是字符串
  • 二、使用 Map
    • 1、Map 常用操作
    • 2、遍历 Map
    • 3、Map 中判断 key 相等
    • 4、复制或合并 Map
    • 5、Map 序列化
  • 三、Map 和 Object 的性能差异

一、别把对象当 Map

1、可能通过原型链访问到未定义的属性

假设现有场景,开发一个网站,需要提供日语、汉语、韩语三种语言,我们可以定义一个字典去管理。

const dictionary = {
    'ja': {
        'Ninjas for hire': '忍者を雇う',
    },
    'zh': {
        'Ninjas for hire': '忍者出租',
    },
    'ko': {
        'Ninjas for hire': '고용 닌자',
    }
}

console.log(dictionary.ja['Ninjas for hire']) // 忍者を雇う
console.log(dictionary.zh['Ninjas for hire']) // 忍者出租
console.log(dictionary.ko['Ninjas for hire']) // 고용 닌자

这样我们就把不同语言的字典管理起来了。但是,当我们试图访问 constroctor 属性,问题就出现了。

console.log(dictionary.ko['constructor']) // ƒ Object() { [native code] }

对于不存在的属性,我们期望得到 undefined,结果却通过原型链访问到了未定义的属性,原型对象的 constructor 属性,指向构造函数。

此处有一个解决办法是把原型设置为 null

Object.setPrototypeOf(dictionary.ko, null)
console.log(dictionary.ko['constructor']) // undefined

2、对象的 Key 只能是字符串

假设需要将对象的 key 映射为 html 节点。我们写如下代码:

/* html部分
<div id="firstElement"></div>
<div id="secondElement"></div>
*/

const firstElement = document.getElementById('firstElement')
const secondElement = document.getElementById('secondElement')

const map = {}

map[firstElement] = {
    data: 'firstElement'
}
map[secondElement] = {
    data: 'secondElement'
}

console.log(map[firstElement].data)     // secondElement
console.log(map[secondElement].data)    // secondElement

第一个元素的数据被覆盖了,原因是对象中的 key 只能是字符串类型,当我们没有使用字符串类型时,它会隐式调用 toString() 函数进行转换。于是两个 html 元素都被转为字符串 [object HTMLDivElement]

对象的键也可以为 Symbol,不过在 for..in 遍历和 Object.keys() 以及用 JSON.stringify() 进行序列化的时候,都会忽略为 Symbol 的键。

二、使用 Map

1、Map 常用操作

Map 可以使用任何 JavaScript 数据类型作为键

function People(name) {
    this.name = name
}
const zhangsan = new People('zhangsan')
const xiaoming = new People('xiaoming')
const lihua = new People('lihua')
// 创建 Map
const map = new Map()
// 创建 Map 并进行初始化 将二维键值对数组转换成一个Map对象
const map1 = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
])
// 将 Map 转为二维数组
console.log(Array.from(map1)) // [ [ 'key1', 'val1' ], [ 'key2', 'val2' ] ]
// 设置键值映射关系
map.set(zhangsan, { region: 'HB' })
map.set(xiaoming, { region: 'HN' })
// 根据 key 获取对应值
console.log(map.get(zhangsan)) // { region: 'HB' }
console.log(map.get(xiaoming)) // { region: 'HN' }
// 获取不存在的 key 得到 undefined
console.log(map.get(lihua))     // undefined
// 通过 has 函数判断指定 key 是否存在
console.log(map.has(lihua))     // false
console.log(map.has(xiaoming))  // true
// map存储映射个数
console.log(map.size)           // 2
// delete 删除 key
map.delete(xiaoming)
console.log(map.has(xiaoming))  // false
console.log(map.size)           // 1
// clear 清空 map
map.clear()
console.log(map.size)           // 0

2、遍历 Map

Map 可以确保遍历的顺序和插入的顺序一致

const zhangsan = { name: 'zhangsan' }
const xiaoming = { name: 'xiaoming' }
const map = new Map()
map.set(zhangsan, { region: 'HB' })
map.set(xiaoming, { region: 'HN' })
// 每个键值对返回的是 [key, value] 的数组
for (let item of map) { // = for (let item of map.entries()) {
    console.log(item)
    // [ { name: 'zhangsan' }, { region: 'HB' } ]
    // [ { name: 'xiaoming' }, { region: 'HN' } ]
}
// 遍历 key
for (let key of map.keys()) {
    console.log(key)
    // { name: 'zhangsan' }
    // { name: 'xiaoming' }
}
// 遍历 value
for (let key of map.values()) {
    console.log(key)
    // { region: 'HB' }
    // { region: 'HN' }
}
// 使用 forEach() 方法迭代 Map
map.forEach(function(value, key) {
    console.log(key, value)
    // { name: 'zhangsan' } { region: 'HB' }
    // { name: 'xiaoming' } { region: 'HN' }
})

3、Map 中判断 key 相等

Map 内部使用 SameValueZero 比较操作。

关于SameValue SameValueZero

SameValue (Object.is()) 和严格相等(===)相比,对于 NaN 和 +0,-0 的处理不同

Object.is(NaN, NaN) // true
Object.is(0, -0) // false

SameValueZero SameValue 的区别主要在于 0 与 -0 是否相等。

map.set(NaN, 0)
map.set(0, 0)
console.log(map.has(NaN))   // true
console.log(map.has(-0))    // true

4、复制或合并 Map

Map 能像数组一样被复制

let original = new Map([
    [1, {}]
])
let clone = new Map(original) // 克隆 Map

console.log(clone.get(1)); // {}
console.log(original === clone) // false
console.log(original.get(1) === clone.get(1)) // true

多个 Map 合并

let first = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three'],
]);
let second = new Map([
    [1, 'uno'],
    [2, 'dos']
]);
// 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的。
// 展开运算符本质上是将 Map 对象转换成数组。
let merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

5、Map 序列化

Map 无法被序列化,如果试图用 JSON.stringify 获得 Map 的 JSON 的话,只会得到 "{}"。

由于 Map 的键可以是任意数据类型,而 JSON 仅允许将字符串作为键,所以一般情况下无法将 Map 转为 JSON。

不过可以通过下面的方式去尝试序列化一个 Map:

// 初始化 Map(1) {"key1" => "val1"}
const originMap = new Map([['key1', 'val1']])
// 序列化 "[[\"key1\",\"val1\"]]"
const mapStr = JSON.stringify(Array.from(originMap.entries()))
// 反序列化 Map(1) {"key1" => "val1"}
const cloneMap = new Map(JSON.parse(mapStr))

三、Map 和 Object 的性能差异

内存占用

不同浏览器的情况不同,但给定固定大小的内存,Map 大约可以比 Object 多存储 50% 的键/值对。

插入性能

Map 略快,如果涉及大量操作,建议使用 Map

查找速度

性能差异极小,但如果只包含少量键/值对,则 Object 有时候速度更快。Object 作为数组使用时浏览器会进行优化。如果涉及大量查找操作,选择 Object 会更好一些。

删除性能

如果代码涉及大量的删除操作,建议选择 Map

到此这篇关于JavaScript 中有了Object 为什么还需要 Map 呢的文章就介绍到这了,更多相关JavaScript  Map 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 实例解析Json反序列化之ObjectMapper(自定义实现反序列化方法)

    对于服务器端开发人员而言,调用第三方接口获取数据,将其"代理"转化并返给客户端几乎是家常便饭的事儿.    一般情况下,第三方接口返回的数据类型是json格式,而服务器开发人员则需将json格式的数据转换成对象,继而对其进行处理并封装,以返回给客户端. 在不是特别考虑效率的情况下(对于搜索.缓存等情形可以考虑使用thrift和protobuffer),通常我们会选取jackson包中的ObjectMapper类对json串反序列化以得到相应对象.通常会选取readValue(Strin

  • Java代码实现Map和Object互转及Map和Json互转

    先给大家介绍下map和object互相转换的代码. 具体代码如所示: /** * 使用org.apache.commons.beanutils进行转换 */ class A { public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception { if (map == null) return null; Object obj = beanClass.newI

  • java中JSONObject转换为HashMap(方法+main方法调用实例)

    1.首先要导入json相关的jar包 引入的jar包: (版本自行定义,可以选用使用人数偏多的版本,这样比较稳定) commons-beanutils-1.9.2.jar commons-collections-3.2.1.jar commons-lang-2.6.jar commons-logging-1.2.jar ezmorph-1.0.6.jar json-lib-2.4-jdk15.jar jar包的下载可以去下面这个网址搜索: https://mvnrepository.com/ 2

  • JavaScript 中有了Object 为什么还需要 Map 呢

    目录 一.别把对象当 Map 1.可能通过原型链访问到未定义的属性 2.对象的 Key 只能是字符串 二.使用 Map 1.Map 常用操作 2.遍历 Map 3.Map 中判断 key 相等 4.复制或合并 Map 5.Map 序列化 三.Map 和 Object 的性能差异 一.别把对象当 Map 1.可能通过原型链访问到未定义的属性 假设现有场景,开发一个网站,需要提供日语.汉语.韩语三种语言,我们可以定义一个字典去管理. const dictionary = { 'ja': { 'Nin

  • JavaScript中利用Array和Object实现Map的方法

    本文实例讲述了JavaScript中利用Array和Object实现Map的方法.分享给大家供大家参考.具体如下: 昨天突然看到以前别人用JavaScript实现的Map感觉很不错,但是发现有个别方法有问题,顺便完善了下,添加了 remove .indexOf .values.clear等方法. /** * @author blune68 * @version 0.1, 07/27/12 * */ function Map(){ this.keys = new Array(); this.dat

  • Json字符串与Object、List、Map的互转工具类

    package com.cq2022.zago.base.util; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.List; import java.util.Map; import org.codehaus.jackson.JsonFactory; import org.codehau

  • java中用ObjectMapper类实现Json与bean的转换示例

    前言 ObjectMapper是jackson中的方法,本文主要给大家介绍了关于java中用ObjectMapper类实现Json与bean转换的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一.将json字符串转为bean public class JsonToJavaBean { public static void main(String[] args) { String str="{\"student\":[{\"name\&q

  • JAVA中JSONObject对象和Map对象之间的相互转换

    1.由json字符串转换成Map对象 如json字符串:{"contend":[{"bid":"22","carid":"0"},{"bid":"22","carid":"0"}],"result":100,"total":2} 下面直接附代码: //json字符串 String json

  • JavaScript中Object、map、weakmap的区别分析

    前言 ECMAScript 6以前,在JavaScript中实现"键/值"式存储可以使用Object来方便高效地完成,也就是使用对象属性作为键,再使用属性来引用值.但这种实现并非没有问题,为此TC39委员会专门为"键/值"存储定义了一个规范.作为ECMAScript 6的新增特性,Map是一种新的集合类型,为这门语言带来了真正的键/值存储机制.Map的大多数特性都可以通过Object类型实现,但二者之间还是存在一些细微的差异.具体实践中使用哪一个,还是值得细细甄别.

  • JavaScript中的数组遍历forEach()与map()方法以及兼容写法介绍

    •原理: •高级浏览器支持forEach方法 语法:forEach和map都支持2个参数:一个是回调函数(item,index,list)和上下文: •forEach:用来遍历数组中的每一项:这个方法执行是没有返回值的,对原来数组也没有影响: •数组中有几项,那么传递进去的匿名回调函数就需要执行几次: •每一次执行匿名函数的时候,还给其传递了三个参数值:数组中的当前项item,当前项的索引index,原始数组input: •理论上这个方法是没有返回值的,仅仅是遍历数组中的每一项,不对原来数组进行

  • JavaScript中你不知道的Object.entries用法

    目录 前言 1. 使用 for...of 遍历普通对象 2. 普通对象与 Map 对象相互转换 总结 参考 前言 平时我们经常会用到 Object 类上的静态方法,例如 Object.keys .Object.values .Object.assign 等等,但可能很少用到 Object.entries 这个方法,这篇文章就来讲解 Object.entries 方法的两个小技巧. 作用 Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for-in

  • JavaScript中的Object对象学习教程

    参数: (1)obj 必需.Object 对象分配到的变量名称.  (2)值 可选.任一 JavaScript 基元数据类型(数字.布尔值或字符串).  如果值是一个对象,则返回的对象是未修改的.  如果值是 null."未定义"或"未提供",则创建无内容的对象. Object对象的方法 Object作为构造函数使用时,可以接受一个参数.如果该参数是一个对象,则直接返回这个对象:如果是一个原始类型的值,则返回该值对应的包装对象.利用这一点,可以写一个判断变量是否为对

  • JavaScript遍历数组的三种方法map、forEach与filter实例详解

    本文实例讲述了JavaScript遍历数组的三种方法map.forEach与filter.分享给大家供大家参考,具体如下: 前言 近一段时间,因为项目原因,会经常在前端对数组进行遍历.处理,JS自带的遍历方法有很多种,往往不加留意,就可能导致知识混乱的现象,并且其中还存在一些坑.前端时间在ediary中总结了js原生自带的常用的对数组遍历处理的方法,分别为:map.forEach.filter,在讲解知识点的同时,会类比相识的函数进行对比,这样会有助于思考方法的利与弊. 一.Js自带的map()

  • JavaScript中的object转换函数toString()与valueOf()介绍

    JavaScript中,object转换为boolean的操作非常简单:所有的object转换成boolean后均为true:即使是new Boolean(false)这样的object在转换为boolean后仍然为true. 复制代码 代码如下: var x = new Boolean(false); if(x){   console.log("x is true"); } 在将object转换为string或者number时,JavaScript会调用object的两个转换函数:t

  • JavaScript创建一个object对象并操作对象属性的用法

    本文实例讲述了JavaScript创建一个object对象并操作对象属性的用法.分享给大家供大家参考.具体分析如下: 下面的JS代码创建了一个myMovie对象,并给对象title和director属性赋值,通过Object定义对象,其属性可以不用定义,直接调用即可,非常简单. <script type="text/javascript"> var myMovie = new Object(); myMovie.title = "Aliens"; myM

  • Javascript 对象(object)合并操作实例分析

    本文实例讲述了Javascript 对象(object)合并操作.分享给大家供大家参考,具体如下: 对象的合并 需求:设有对象 o1 ,o2,需要得到对象 o3 var o1 = { a:'a' }, o2 = { b:'b' }; // 则 var o3 = { a:'a', b:'b' } 方法1:使用JQuery的extend方法 **方法定义**:jQuery.extend([deep], target, object1, [objectN]) > 用一个或多个其他对象来扩展一个对象,返

  • 详解JavaScript中的Object.is()与"==="运算符总结

    三重相等运算符 === 严格检查2个值是否相同: 1 === 1; // => true 1 === '1'; // => false 1 === true; // => false 但是,ES2015规范引入了 Object.is(),其行为与严格的相等运算符几乎相同: Object.is(1, 1); // => true Object.is(1, '1'); // => false Object.is(1, true); // => false 主要问题是:什么时

随机推荐