发布于 2026-01-06 3 阅读
0

JavaScript 地图入门 - 你需要知道的一切

JavaScript 地图入门 - 你需要知道的一切

你听说过 JavaScript 中的 Map 吗?Map 是 ES2015 中引入的一种新的对象类型。在本教程中,你将学习关于这种不太常见的对象类型的所有知识。你将了解 JavaScript 中的 Map 是什么,它们如何工作以及如何使用它们。

地图简介

作为一名 JavaScript 开发者,你可能已经了解 JavaScript对象。对象允许你以键值对的形式存储数据。映射(Map)与 JavaScript 对象非常相似。当你想在映射中存储一些数据时,你也需要以键值对的形式存储这些数据。

就像对象一样,你也可以在映射中添加新键、删除现有键,并从中检索它们的值。映射和对象之间存在一些区别,你应该了解这些区别。在继续之前,让我们先来看看这些区别。

地图与对象

其中一个最重要的区别在于,在 JavaScript 的 Map 中,你可以使用任何数据类型来创建键。你甚至可以使用对象或函数。而对象则只允许使用字符串符号。另一个重要的区别是键值对的顺序。

在映射(map)中,键的顺序取决于你将它们添加到映射中的顺序。如果你遍历一个映射,你会得到与创建顺序相同的键。对于对象而言,自 ES2015 起,并且仅适用于支持此规范的 JavaScript 引擎,这种顺序才成立。在 ES2015 之前,对象中的键是无序的。

另一个区别在于获取映射表大小的便捷性。与集合类似,每个映射表都有一个size属性,用于指示它包含多少个键值对。而对于对象,你需要使用 ` get_keys()`keys()values()`get_values()` 来获取键或值的数组。然后,使用 `get_length()`length来获取该数组的长度,最终才能得到对象的大小。

另一个优点是,映射(map)和数组一样,都是可迭代的。你不需要先获取键或值才能进行迭代,可以直接操作。例如,你可以像处理数组一样使用`forEach()` 方法。你也可以像处理对象一样使用`for...of` 循环。

最后一个值得注意的区别是,映射(Map)针对键值对的添加和删除进行了优化,而对象(Object)则没有。如果您不需要频繁地操作数据,这可能无关紧要。但如果您需要频繁操作数据,使用映射(Map)或许有助于提升 JavaScript 代码的性能。

在 JavaScript 中创建地图

映射与对象类似。除了我们刚才讨论的那些区别之外,它们之间的一个显著区别在于创建方式。创建新对象时,有多种方法可供选择。例如,可以使用 `new Map` new Object()Object.create()`new Object`、对象字面量或对象构造函数。

创建新地图有两种方法。理论上是这样。第一种方法是创建一个新的空 Map 对象,new Map()然后再为其赋值。

// Creating new map
let myMap = new Map()

从数组到映射

第二种方法也是new Map()创建新的 Map 对象。不过,你也可以传入一个数组。为了使这种方法奏效,这个数组必须按照特定的结构来组织。它必须包含一个嵌套数组,每个数组对应一个键值对。每个数组(键值对)必须包含两个元素:键和值。

// Create new map and assign it some values right away
const myMap = new Map([
  ['name',  'Jackie'],
  ['gender', 'female'],
  ['age', 23]
])

// Log the content of "myMap" map
console.log(myMap)
// Output:
// Map { 'name' => 'Jackie', 'gender' => 'female', 'age' => 23 }

从对象到地图

你也可以对对象使用第二种方法。你可以获取一个现有对象,并使用entries()该方法获取其所有条目。该entries()方法返回的条目格式与你在上一个示例中看到的数组格式相同。因此,你可以将调用该方法的结果传递entries()Map()对象。

// Create new object
const myObj = {
  subject: 'Math',
  level: '1',
  difficulty: 'Medium'
}

// Create new map from "myObj"
const myMap = new Map(Object.entries(myObj))

// Log the content of "myMap" map
console.log(myMap)
// Outputs:
// Map { 'subject' => 'Math', 'level' => '1', 'difficulty' => 'Medium' }


// Or, a bit longer
// Create new object
const myObj = {
  subject: 'Math',
  level: '1',
  difficulty: 'Medium'
}

// Get all entries
const myObjEntries = Object.entries(myObj)

// Create new map from "myObjEntries"
const myMap = new Map(myObjEntries)

// Log the content of "myMap" map
console.log(myMap)
// Outputs:
// Map { 'subject' => 'Math', 'level' => '1', 'difficulty' => 'Medium' }

向地图添加值

当你想向对象添加值(键值对)时,有两种方法。如果算上对象初始化时添加值的方法,那就是三种了。第一种方法是使用 do 表示法。第二种方法是使用方括号。第二种方法(方括号)也适用于映射(map)。

也就是说,使用方括号向映射中添加值并不是一个好的做法。这样做会丢失映射的一些性能优化。向映射中添加值的正确方法是使用 `addValues`set()方法。该方法接受两个参数。第一个参数用于指定映射的元素key,第二个参数用于指定映射的元素value

// Create new map
const myMap = new Map()

// Create simple function
function sayHi() {
  return 'Hello!'
}

// Add some values (key-value pairs) to "myMap"
myMap.set('name', 'James Reacher')
myMap.set('bio', { age: 35, height: 189, weight: 82 })
myMap.set(sayHi, 'Function as a key?')

// Log the content of "myMap"
console.log(myMap)
// Output:
// Map {
//   'name' => 'James Reacher',
//   'bio' => { age: 35, height: 189, weight: 82 },
//   [Function: sayHi] => 'Function as a key?'
// }

当你想向映射表中添加多个键值对时,一次只能添加一个。有趣的是,映射表支持链式调用。所以,没错,你需要set()为每个要添加的键值对使用一个方法。但是,你可以将这些方法链接起来,这样就不用一遍又一遍地使用映射表的名称了。

// Create new map
const myMap = new Map()

// Add some values using chaining
myMap.set('Language', 'JavaScript')
  .set('Author', 'Brendan Eich')
  .set('First appeared', '1995')

// Log the content of "myMap"
console.log(myMap)
// Output:
// Map {
//   'Language' => 'JavaScript',
//   'Author' => 'Brendan Eich',
//   'First appeared' => '1995'
// }

从地图中移除值

当你想从映射中删除值时,过程很简单。你需要使用一个名为 `remove` 的方法delete()。该方法接受一个参数,即你要删除的键值对的键。如果删除成功,该delete()方法将返回 `true` true。如果键不存在,它将返回 `false` false

需要记住的一点delete()是,该方法一次只能处理一个键。你不能传入多个键。如果你尝试这样做,该delete()方法只会移除第一个键,其余的键都会被忽略。

// Create new map
const myMap = new Map()

// Add some values to "myMap"
myMap.set('name', 'Joe')
myMap.set('age', 25)

// Log the content of "myMap"
console.log(myMap)
// Output:
// Map { 'name' => 'Joe', 'age' => 25 }

// Remove "name" from "myMap"
myMap.delete('name')

// Log the content of "myMap" again
console.log(myMap)
// Output:
// Map { 'age' => 25 }


// This will not work
// Create new map
const myMap = new Map()

// Add some values to "myMap"
myMap.set('name', 'Joe')
myMap.set('age', 25)

// Try to remove "name" and "age" at the same time
myMap.delete('name', 'age')

// Log the content of "myMap" again
// Hint: only the "name" will be removed
// because it was the first parameter
console.log(myMap)
// Output:
// Map { 'age' => 25 }

从地图中移除所有值

当您只想删除一个或几个值时,使用 `remove`delete()方法很方便。但如果您想一次性删除映射中的所有值,还有一种更好更快的方法。除了覆盖映射之外,您还可以使用 `remove`clear()方法。此方法不接受任何参数。

// Create new map
const myMap = new Map()

// Add some values to "myMap"
myMap.set('The Lean Startup', 'Eric Ries')
myMap.set('Measure What Matters', 'John Doerr')
myMap.set('The Startup Owner\'s Manual', 'Steve Blank')

// Log the content of "myMap"
console.log(myMap)
// Output:
// Map {
//   'The Lean Startup' => 'Eric Ries',
//   'Measure What Matters' => 'John Doerr',
//   "The Startup Owner's Manual" => 'Steve Blank'
// }

// Remove all values from "myMap"
myMap.clear()

// Log the content of "myMap"
console.log(myMap)
// Output:
// Map {}

从地图中检索值

在映射中添加和删除值非常简单,检索值也是如此。要从映射中检索特定值,可以使用 `getValue()`get()方法。此方法接受一个参数,即与要检索的值关联的键。

如果要检索的键及其值存在,则返回该值。如果不存在,则返回空值undefined

// Create new map
const myMap = new Map()

// Add some values to "myMap"
myMap.set('front-end', 'React')
myMap.set('back-end', 'Node.js')
myMap.set('database', 'MongoDB')

// Get the value of "back-end" key
myMap.get('back-end')
// Output:
// 'Node.js'

// Try to get the value of non-existent key "cms"
myMap.get('cms')
// Output:
// undefined

检查映射中是否存在该值

从某种意义上说,该get()方法也可以帮助您检查映射中是否存在某个键。不过,有一个专门用于此目的的方法,名为 `getKey()` has()。与 `getKey()` 类似get(),`getKey()`has()方法也接受一个参数,即key您要查找的键。如果键存在,has()则返回 `true`;true否则,返回 `false` false

// Create new map
const myMap = new Map()

// Add some values to "myMap"
myMap.set('language', 'English')

// Check if "myMap" has "language" key
myMap.get('language')
// Output:
// true

// Check if "myMap" has "compiler" key
myMap.get('compiler')
// Output:
// false

获取地图的大小

在“Map 与 Objects”一文中,我们讨论过 Map 的优势之一在于可以轻松获取其大小。的确如此。每个 Map 对象都有自己的size属性。该属性类似于数组的lengthsize属性。使用此属性可以快速了解特定 Map 中包含多少个键值对。

// Create new map
const myMap = new Map()

// Log the size of "myMap"
console.log(myMap.size)
// Output:
// 0

// Add some values to "myMap"
myMap.set('Tony Stark', 'Iron Man')
  .set('Steve Rogers', 'Captain America')
  .set('Black Widow', 'Natasha Romanoff')
  .set('Bruce Banner', 'Hulk')

// Log the size of "myMap" again
console.log(myMap.size)
// Output:
// 4

遍历地图

您知道如何向映射中添加值以及如何删除值。您也知道如何逐个检索值。问题是,如果您想从映射中检索所有值该怎么办?您可以选择四种方法。这些方法分别是 `addValues`、`removeValues` keys()values()` removeValues`entries()forEach()`removeValues`。

Map.keys()、Map.values() 和 Map.entries()

前三个方法都返回Iterator包含特定数据的对象。第一个方法keys()返回一个Iterator键值对对象,每个键对应 Map 中的一对数据。第二个方法返回一个值values() Iterator对对象,每个值也对应 Map 中的一对数据。第三个方法entries()返回一个包含所有条目的可迭代对象。

这些条目以映射的形式返回[key, value]。使用这三种方法后,您可以使用`get` 方法及其属性遍历返回的Iterator对象。每次使用` get` 方法以及 `get` 属性,都会返回迭代器中的下一个值、映射中的下一个键或条目。next()valuenext()value

// Create new map
const myMap = new Map()

// Add some values
myMap.set('First name', 'Joshua Doer')
myMap.set('Email', 'joshua@doer.io')
myMap.set('username', 'josh1234')


// Example no.1: Map.keys()
// Create iterator for keys
const myKeysIterator = myMap.keys()

// Log the first key
console.log(myKeysIterator.next().value)
// Output:
// 'First name'

// Log the second key
console.log(myKeysIterator.next().value)
// Output:
// 'Email'

// Log the third key
console.log(myKeysIterator.next().value)
// Output:
// 'username'


// Example no.2: Map.values()
// Create iterator for values
const myValuesIterator = myMap.values()

// Log the first value
console.log(myValuesIterator.next().value)
// Output:
// 'Joshua Doer'

// Log the second value
console.log(myValuesIterator.next().value)
// Output:
// 'joshua@doer.io'

// Log the third value
console.log(myValuesIterator.next().value)
// Output:
// 'josh1234'


// Example no.3: Map.entries()
// Create iterator for entries
const myEntriesIterator = myMap.entries()

// Log the first entry
console.log(myEntriesIterator.next().value)
// Output:
// [ 'First name', 'Joshua Doer' ]

// Log the second entry
console.log(myEntriesIterator.next().value)
// Output:
// [ 'Email', 'joshua@doer.io' ]

// Log the third entry
console.log(myEntriesIterator.next().value)
// Output:
// [ 'username', 'josh1234' ]

映射、迭代器和 for...of 循环

如果你想一次性获取对象中的所有数据,使用 ` next()and`value并不是最佳选择。更好的方法是使用循环。循环允许你遍历对象并获取其中的所有数据,而无需多次使用 `and`。Iteratorfor...ofIteratornext()

// Create new map
const myMap = new Map()

// Add some values
myMap.set('First name', 'Joshua Doer')
myMap.set('Email', 'joshua@doer.io')
myMap.set('username', 'josh1234')


// Create iterator for entries
// NOTE: this will work in the same way
// also for keys() and values()
const myEntriesIterator = myMap.entries()

// Loop over the iterate object "myEntriesIterator"
for (let iteratorItem of myEntriesIterator) {
  // Log each item in the iterator
  console.log(iteratorItem)
}
// Output:
// [ 'First name', 'Joshua Doer' ]
// [ 'Email', 'joshua@doer.io' ]
// [ 'username', 'josh1234' ]

Map.forEach()

这种forEach()方法略有不同。它不会返回类似 `{{value}`和 ` Iterator{{value1}` 这样的对象keys()也不允许你手动遍历这些值。相反,它会直接遍历映射表。它还会自动遍历所有键值对。values()entries()forEach()

当它遍历所有键值对时,会为每一对键值对执行一个回调函数。这个回调函数接受三个参数,这些参数都是可选的。这三个参数分别是 `current` valuekey`current_value` 和`current_value`。`current` 参数允许你在每次迭代中访问当前键值map对。valuevalue

key允许你访问当前key迭代中的位置。最后一个,即map,允许你访问你正在迭代的整个映射。

// Create new map
const myMap = new Map()

// Add some values
myMap.set('title', 'JavaScript: The Definitive Guide')
myMap.set('author', 'David Flanagan')
myMap.set('publisher', 'O\'Reilly Media')

// Loop over "myMap" map directly
myMap.forEach((value, key) => {
  // Log key and value in the map
  console.log(`${key}: ${value}`)
})
// Output:
// 'title: JavaScript: The Definitive Guide'
// 'author: David Flanagan'
// "publisher: O'Reilly Media"

从地图到物体

你知道可以从对象创建映射。反过来也可以,你可以使用现有的映射来创建一个新对象。这可以通过fromEntries()方法实现。一开始,你使用entries()该方法从对象内部的条目创建了一个数组。

fromEntries()方法的作用正好相反。它接受一个数组(格式为 `map` [key, value]),并将其转换为对象。`map`entries()方法可以帮助你将任何映射转换为所需的数组。然后,你可以使用该数组和 `map`fromEntries()方法创建新对象。

// Create new map
const myMap = new Map()

// Add some values
myMap.set('spanish', 'Buenos dias')
myMap.set('french', 'Bonjour')
myMap.set('russian', 'Доброе утро')

// Transform the map into an array
const myTransformedMap = myMap.entries()

// Create new object from transformed map
const myObj = Object.fromEntries(myTransformedMap)

// Log the content of "myObj"
console.log(myObj)
// Output:
// {
//   spanish: 'Buenos dias',
//   french: 'Bonjour',
//   russian: 'Доброе утро'
// }

结论:JavaScript 中的地图简介

Map 是 JavaScript 中一种不太为人所知且使用频率较低的对象类型。我希望这篇教程能帮助你了解 JavaScript 中的 Map 是什么、它们如何工作以及如何使用它们。我也希望它能帮助你区分在什么情况下 Map 比 Object 更合适。

文章来源:https://dev.to/alexdevero/introduction-to-maps-in-javascript-all-you-need-to-know-52jg