用map代替纯JavaScript对象入门指南_map小白攻略

JavaScript 普通对象 {key: ‘value’} 可用于保存结构化数据。但是我发现很烦人的一件事:对象的键必须是字符串(或很少使用的符号)。如果用数字作键会怎样?在这种情况下没有错误:const names = {1: ‘One’,2: ‘Two’,
};Object.keys(names); // => [‘1’, ‘2’]JavaScript

用map代替纯JavaScript对象入门指南

JavaScript 普通对象 {key: ‘value’} 可用于保存结构化数据。

用map代替纯JavaScript对象入门指南_map小白攻略

但是我发现很烦人的一件事:对象的键必须是字符串(或很少使用的符号)。

如果用数字作键会怎样?在这种情况下没有错误:

const names = {
  1: 'One',
  2: 'Two',
};

Object.keys(names); // => ['1', '2']

JavaScript 只是将对象的键隐式转换为字符串。这是一件棘手的事,因为你失去了类型的一致性。

在本文中,我将介绍 ES2015 中提供的 JavaScript Map 对象如何解决许多普通对象的问题,包括将键转换为字符串。

1. map 可接受任意类型的键

如上所述,如果对象的键不是字符串或符号,则 JavaScript 会将其隐式转换为字符串。

幸运的是,map 在键类型上不存在问题:

const numbersMap = new Map();

numbersMap.set(1, 'one');
numbersMap.set(2, 'two');

[...numbersMap.keys()]; // => [1, 2]

1 和 2 是 numbersMap 中的键。这些键的类型 number 保持不变。

你可以在 map 中使用任何键类型:数字,布尔以及经典的字符串和符号。

const booleansMap = new Map();

booleansMap.set(true, "Yep");
booleansMap.set(false, "Nope");

[...booleansMap.keys()]; // => [true, false]

booleansMap 用布尔值作为键没有问题。

同样,布尔键在普通对象中不起作用。

让我们超越界限:你能把整个对象用作 map 中的键吗?当然可以!

1.1 把对象做为键

假设你需要存储一些与对象相关的数据,但是不把这些数据附加到对象本身。

不能用普通对象这样做。

一种解决方法是用一组对象值元组:

const foo = { name: 'foo' };
const bar = { name: 'bar' };

const kindOfMap = [
  [foo, 'Foo related data'],
  [bar, 'Bar related data'],
];

kindOfMap 是一个包含一对对象和关联值的数组。

这种方法的最大问题是通过键访问值的时间复杂度为 O(n) 。你必须遍历整个数组才能通过键获得所需的值:

function getByKey(kindOfMap, key) {
  for (const [k, v] of kindOfMap) {
    if (key === k) {
      return v;
    }
  }
  return undefined;
}

getByKey(kindOfMap, foo); // => 'Foo related data'

用 WeakMap(Map 的专用版本)你无需为此烦恼。它接受把对象作为键。

Map 和 WeakMap 之间的主要区别是后者允许对作为键的对象进行垃圾回收,从而防止内存泄漏。

把上面的代码重构为使用 WeakMap 的代码付出的代价微不足道:

const foo = { name: 'foo' };
const bar = { name: 'bar' };

const mapOfObjects = new WeakMap();

mapOfObjects.set(foo, 'Foo related data');
mapOfObjects.set(bar, 'Bar related data');

mapOfObjects.get(foo); // => 'Foo related data'

与 Map 相对,WeakMap 仅接受把对象作为键,并具有精简的方法集。

2. map 对键名没有限制

JavaScript 中的任何对象都从其原型对象继承属性。普通的 JavaScript 对象也是如此。

如果覆盖从原型继承的属性,则可能会破坏依赖于这些原型属性的代码:

function isPlainObject(value) {
  return value.toString() === '[object Object]';
}

const actor = {
  name: 'Harrison Ford',
  toString: 'Actor: Harrison Ford'
};

// Does not work!
isPlainObject(actor); // TypeError: value.toString is not a function

在对象 actor 上定义的属性 toString 覆盖了从原型继承的 toString() 方法。因为它依赖于 toString() 方法,所以这破坏了 isObject()。

检查普通对象从原型继承的属性和方法列表。要避免使用这些名称定义自定义属性。

例如,假设有一个管理某些自定义字段的用户界面。用户可以通过指定名称和值来添加字段:

用map代替纯JavaScript对象入门指南_map小白攻略

将自定义字段的状态存储到一个普通对象中会很方便:

const userCustomFields = {
  'color':    'blue',
  'size':     'medium',
  'toString': 'A blue box'
};

但是用户可能会选择一个自定义字段名称,例如 toString(如例中所示), constructor 等,这可能会破坏你的对象。

不要通过接受用户的输入在普通对象上创建键!

map 则没有这个问题。键的名称不受限制:

function isMap(value) {
  return value.toString() === '[object Map]';
}

const actorMap = new Map();

actorMap.set('name', 'Harrison Ford');
actorMap.set('toString', 'Actor: Harrison Ford');

// Works!
isMap(actorMap); // => true

不管 actorMap 是否具有名为 toString 的属性,方法 toString() 都能正常工作。

3. map 是可迭代的

为了遍历普通对象的属性,你必须用其他辅助静态函数,例如 Object.keys() 或 Object.entries() (在 ES2017 中可用):

const colorsHex = {
  'white': '#FFFFFF',
  'black': '#000000'
};

for (const [color, hex] of Object.entries(colorsHex)) {
  console.log(color, hex);
}
// 'white' '#FFFFFF'
// 'black' '#000000'

Object.entries(colorsHex) 返回从对象提取的键值对数组。

但是,map 本身是可迭代的:

const colorsHexMap = new Map();

colorsHexMap.set('white', '#FFFFFF');
colorsHexMap.set('black', '#000000');

for (const [color, hex] of colorsHexMap) {
  console.log(color, hex);
}
// 'white' '#FFFFFF'
// 'black' '#000000'

colorsHexMap 是可迭代的。你可以在任何可迭代的地方使用它:for() 循环,展开运算符 […map] 等。

map 还提供了返回迭代的其他方法:map.keys() 遍历键,map.values() 遍历值。

4. map的大小

普通对象的另一个问题是你无法轻松确定其拥有的属性数量:

const exams = {
  'John Smith': '10 points',
  'Jane Doe': '8 points',
};

Object.keys(exams).length; // => 2

要确定 exams 的大小,你必须通过它所有键来确定它们的数量。

map 提供了一种替代方法,通过它的访问器属性 size 计算键值对:

const examsMap = new Map([
  ['John Smith', '10 points'],
  ['Jane Doe', '8 points'],
]);
  
examsMap.size; // => 2

确定 map 的大小​​更加简单:examsMap.size。

5.结论

普通的 JavaScript 对象通常可以很好地保存结构化数据。但是它们有一些限制:

  1. 只能用字符串或符号用作键
  2. 自己的对象属性可能会与从原型继承的属性键冲突(例如,toString,constructor 等)。
  3. 对象不能用作键

所有这些问题都可以通过 map 轻松解决。而且它们提供了诸如迭代器和易于进行大小查找之类的好处。

不要将 map 视为普通对象的替代品,而应视为补充。

海计划公众号
(0)
上一篇 2020/03/26 23:30
下一篇 2020/03/26 23:30

您可能感兴趣的内容

  • JS 判断元素是否可以滚动菜鸟教程网_滚动菜鸟教程网

    今天在解决 ios 移动端滚动穿透的问题时遇到一个问题,就是判断元素能否滚动,把这个过程记录下来。以下以纵向滚动为例,横向滚动同理。基础概念Element.scrollHeightscrollHeight 是一个元素内容高度的度量,包括由于溢出导致的视图中不可见的内容,scrollHeight 的值等于元素在不能出现滚动条的时候展示出视图中所有内容所需要的最

    2020/03/20
  • ok-admin菜鸟教程网_一个很赞的,扁平化风格的,响应式布局的后台管理模版

    ok-admin菜鸟教程网 官方网址:http://ok-admin.xlbweb.cn 简介描述:一个很赞的,扁平化风格的,响应式布局的后台管理模版 ok-admin v2.0 …

    2020/03/12
  • 什么是流式计算?菜鸟知识_计算新手入门

    一、流式计算的背景在日常生活中,我们通常会先把数据存储在一张表中,然后再进行加工、分析,这里就涉及到一个时效性的问题。如果我们处理以年、月为单位的级别的数据,那么多数据的实时性要求并不高;但如果我们处理的是以天、小时,甚至分钟为单位的数据,那么对数据的时效性要求就比较高。在第二种场景下,如果我们仍旧采用传统的数据处理方式,统一收集数据,存储到数据库中,之后在

    2020/03/26
  • iOS Icon Gallery 教程视频iOS App 图标欣赏的网站

    iOS Icon Gallery基础入门 官方网址:http://www.iosicongallery.com/ 简介描述:iOS App 图标欣赏的网站 Showcasing b…

    2020/03/05
  • js base64的实现新手入门_base64菜鸟知识

    什么是base64base64是用规定的64种字符来表示任意二进制数据的一种编码格式,而且这64种字符均是可见字符,而之所以要是可见的是因为在不同设备上处理不可见字符时可能发生错误。通常,电子邮件数据、公钥证书会经常使用。base64编码不提供加密,只是将一种形式的数据转化为另一种形式。Base64编码使用二进制表示,字符串的每一个字符由8个字节表示。Js实

    2020/03/30
  • minigrid教程视频最小的响应式动态网格瀑布流布局js插件

    minigrid基础入门 官方网址:http://minigrid.js.org/ GitHub:https://github.com/henriquea/minigrid 简介描…

    2020/03/05
  • JavaScript的内存模型使用教程_内存基础指南

    引言在我们的前端日常工作中,无时无刻不在进行着变量的声明和赋值,你是否也曾碰到过变量声明报错或变量被污染的问题,如果你跟笔者一样碰到过,那么我们应该暂时停下来好好思考问题发生的原因以及如何采取相应的补救措施。当然排查问题最好的方式就是深入其底层细节,了解在JavaScript中的内存分配方式。只有我们对底层细节有一定的了解之后,才能轻而易举地化解在写代码过程

    2020/03/23
  • javascript如何计算两个日期的时间差?基础指南_日期入门知识

    JavaScript可以使用getTime()方法返回两个日期的格林威治时间数值,将此数值相减即可的到两个日期之间的毫秒数,通过两个时间差的毫秒数即可得到相差的时间。在后台传来两个时间字段,从中解析出两个字符串类型的日期格式 需要在前台解析出两个时间的间隔。这里采用获取两个日期的时间戳进行计算。时间戳是指格林威治时间1970年01月01日00时00分00秒(

    2020/03/22
  • 为什么要初始化css样式?使用指南_样式新手入门

    为什么要初始化css样式1.浏览器差异不同浏览器对有些标签的默认值是不同的,如果没对css初始化会出现浏览器之间的页面显示差异2.提高编码质量如果不初始化,整个页面做完会很糟糕,重复的css样式很多最简单的初始化方法是:(不建议)* {padding: 0; margin: 0;}淘宝样式 样式初始化body, h1, h2, h3, h4, h5, h6,

    2020/03/29
  • store入门知识_使用localStorage和sessionStorage的更好方法

    store入门知识 GitHub:https://github.com/nbubna/store 简介描述:使用localStorage和sessionStorage的更好方法 一…

    2020/03/11
  • Jiko入门基础教程_现代化的,易于使用的面向Javascript的模板引擎

    Jiko入门基础教程 官方网址:http://jiko.neoname.eu/ GitHub:https://github.com/nicolas-van/jiko 简介描述:现代…

    2020/03/06
  • css隐藏滚动条同时可以滚动新手入门_滚动使用说明

    1. 通过 ::-webkit-scrollbar 伪元素.inner-container::-webkit-scrollbar {display: none;
    }简单粗暴,但是兼容性不好2. 外层元素 overflow: hidden 内层元素absolute定位// css 样式
    .element, .outer-container {wi

    2020/03/29
  • web前端程序员真的这么值钱吗?小白教程_前端使用说明

    对于互联网公司来说用户就是上帝,做好客户体验一切才有可能。所以互联网公司都会把钱砸向前端,Web前端程序员也越来越受到企业争相聘用。前端工程师工资也越来越高,目前Web前端工程师工作1~2年后通常会成为Web前端高级软件工程师,年薪可以达到15万以上;工作3-5年后通常可以成为Web前端技术主管或者经理,年薪在15-50万之间;工作年限5年以上,通常会成为互

    2020/03/30
  • Core.css基础入门_一个用于构建响应式网站的轻量级框架

    Core.css基础入门 官方网址:http://www.corecss.io/ 简介描述:一个用于构建响应式网站的轻量级框架 Core.css 是一个用于构建响应式网站的轻量级框…

    2020/03/06
  • IPv6特点;IPv6与IPv4共存技术小白入门_协议基础入门

    实践证明IPv4是一个非常成功的协议,它本身也经受住了Internet从数目很少的计算机发展到目前上亿台计算机互联的考验。但该协议是几十年前基于当时的网络规模而设计的。在今天看来,IPv4的设计者们对于Internet的估计和预想显得很不充分。随着Internet的扩张和新应用的不断推出,IPv4越来越显示出它的局限性。Internet规模的快速扩大是当时完

    2020/03/29
  • 微信小程序:禁止弹框底部内容滑动入门指南_弹框指南教程

    我们需要解决的问题:当弹框显示的时候,弹框下面的内容不能滚动小程序的弹框特别多,弹框的底部是一个可以下拉的页面,但是当弹框出现的时候,需要禁止底部的滚动,那么怎么做呢?一:给view加上catchtouchmove=’true’ 比如下面这种:至于加到哪个view上边,自然是最外边包含整

    2020/03/26