重学JavaScript - 映射与集合
作者:狐狸家的鱼
GitHub:surRimn
整理自MDN文档
映射
Map对象
一个Map
对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 for...of
循环在每次迭代后会返回一个形式为[key,value]的数组。
1、语法
new Map([iterable])
iterable
可以是一个数组或者其他iterable
对象,其元素或为键值对,或为两个元素的数值。每个键值对都添加到新的Map
。null
会被当作undefined
。
2、属性
Map.length
:属性length
的值为0get Map[@@species]
:构造函数用于创建派生对象Map.prototype
:表示Map
构造器的原型,允许添加属性从而用于所有的Map
对象
3、键的相等
键的比较是基于 "SameValueZero
" 算法:NaN
是与 NaN
相等的(虽然 NaN !== NaN
),剩下所有其它的值是根据 ===
运算符的结果判断是否相等。+0 和-0是相同的值。
4、Object 和 Map的比较
Object会被用于将字符串类型映射到数值。Object
允许设置键值对、根据键获取值、删除键、检测某个键是否存在
-
必须手动计算
Object
的尺寸,但是可以使用.size
获取使用Map
的尺寸 -
Map
的遍历遵循元素的插入顺序 -
Map
可直接进行迭代,而Object
的迭代需要先获取它的键数组,然后再进行迭代 -
Object
有原型,所以映射中有一些缺省的键。(可以用map = Object.create(null) 回避
) -
Map
在涉及频繁增删键值对的场景下会有性能优势
这三条提示可以帮你决定用Map
还是Object
:
-
如果键在运行时才能知道,或者所有的键类型相同,所有的值类型相同,那就使用
Map
-
如果需要将原始值存储为键,则使用
Map
,因为Object
将每个键视为字符串,不管它是一个数字值、布尔值还是任何其他原始值 -
如果需要对个别元素进行操作,使用
Object
5、Map实例
所有的 Map
对象实例都会继承 Map.prototype
(1)属性
-
Map.prototype.constructor
:返回一个函数,它创建了实例的原型,默认是Map
函数 -
Map.prototype.size
:返回Map
对象的键/值对的数量
(2)方法
方法 | 功能 |
---|---|
Map.prototype.clear() |
移除Map对象的所有键/值对 |
Map.prototype.delete(key) |
如果 Map 对象中存在该元素,则移除它并返回 true ;否则如果该元素不存在则返回 false |
Map.prototype.get(key) |
返回键对应的值,如果不存在,则返回undefined |
Map.prototype.has(key) |
返回一个布尔值,表示Map实例是否包含键对应的值 |
Map.prototype.set(key) |
设置Map对象中键的值。返回该Map对象 |
Map.prototype.keys() |
返回一个新的 Iterator 对象, 它按插入顺序包含了Map对象中每个元素的键 |
Map.rpototype.values() |
返回一个新的Iterator 对象,它按插入顺序包含了Map对象中每个元素的值 |
Map.prototype.entries() |
返回一个新的 Iterator 对象,它按插入顺序包含了Map对象中每个元素的 [key, value]**数组** |
Map.prototype.forEach(callbackFn[, thisArg]) |
按插入顺序,为 Map 对象里的每一键值对调用一次callbackFn 函数。如果为forEach 提供了thisArg ,它将在每次回调中作为this值 |
Map.prototype[@@interator]() |
返回一个新的Iterator 对象,它按插入顺序包含了Map对象中每个元素的 [key, value]**数组** |
(3)示例
使用Map对象
let sayings = new Map(); sayings.set('dog', 'woof'); sayings.set('cat', "meow"); sayings.set('elephant', 'toot'); console.log(sayings.size);//3 sayings.has('fox'); sayings.get('dog'); sayings.delete('dog'); sayings.has('dog'); for (let [key, value] of sayings) { console.log(key + " goes " + value); } sayings.clear(); console.log(sayings.size);//0 //打印台信息: 3 cat goes meow elephant goes toot 0
将 NaN
作为 Map
的键
NaN
也可以作为Map
对象的键。虽然 NaN
和任何值甚至和自己都不相等(NaN !== NaN
返回true)
//将NaN作为Map的键 let myMap = new Map(); myMap.set(NaN, "not a number"); console.log(myMap.get(NaN));//not a number let otherNaN = Number("foo"); console.log(myMap.get(otherNaN));//not a number
迭代Map
//for..of迭代 for (let [key, value] of sayings) console.log(key + " goes " + value); for (let key of sayings.keys()) console.log(key); for (let value of sayings.values()) console.log(value); for (let [key, value] of sayings.entries()) console.log(key + " goes " + value); //打印台信息: cat goes meow elephant goes toot cat elephant meow toot cat goes meow elephant goes toot //forEach()迭代 sayings.forEach((value, key) => { console.log(key + " goes " + value); }, sayings) //打印台信息: cat goes meow elephant goes toot
Map与数组的关系
let arr = [ ["key1", "value1"], ["key2", "value2"] ] //使用Map构造函数将一个二维键值对数组转换成一个Map对象 let newMap = new Map(arr); console.log(newMap.get("key1")); //使用Array.from函数将一个Map对象转换成一个二维键值对数组 console.log(Array.from(newMap)); //在键或值的迭代器上使用Array.from,得到只含有键或值的数组 console.log(Array.from(newMap.keys())) //打印台信息: value1 [ [ 'key1', 'value1' ], [ 'key2', 'value2' ] ] [ 'key1', 'key2' ]
复制或合并Maps
Map
能像数组一样被复制
let original = new Map([ [1, 'one'] ]) let clone = new Map(original); console.log(clone.get(1));//one console.log(original === clone);//false
PS:数据本身未被克隆。
Map
合并两个Map
对象时,如果有重复的键值,则后面的会覆盖前面的。 展开运算符本质上是将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
集合
Set对象
Set
对象是一组不重复的值的集合,可以按照添加顺序来遍历。Set中的元素只会出现一次,即 Set 中的元素是唯一的。
1、语法
new Set([iterable])
如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set中。如果不指定此参数或其值为null,则新的 Set为空。
返回值为一个新的Set
对象。
2、值的相等
对于 Set s, +0 (+0 严格相等于-0)和-0是不同的值。NaN
和undefined
都可以被存储在Set 中, NaN
之间被视为相同的值(尽管 NaN !== NaN
)
3、属性
-
Set.length
:length
属性的值为0 -
get Set[@@species]
:构造函数用来创建派生对象 -
Set.prototype
:表示Set
构造器的原型,允许向所有Set
对象添加新的属性
4、Set实例
(1)属性
-
Set.prototype.constructor
:返回实例的构造函数,默认是Set
-
Set.prototype.size
:返回Set
对象的键/值对的数量
(2)方法
方法 | 功能 |
---|---|
Set.prototype.clear() |
移除Set 对象内的所有元素 |
Set.prototype.delete(key) |
移除Set的中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)。``Set.prototype.has(value)在此后会返回false |
Set.prototype.add(key) |
在Set对象尾部添加一个元素。返回该``Set对象 |
Set.prototype.has(key) |
返回一个布尔值,表示该值在Set中存在与否 |
Set.prototype.keys() |
与values()方法相同,返回一个新的迭代器对象,该对象包含Set对象中的 按插入顺序排列的所有元素的值 |
Set.rpototype.values() |
返回一个新的迭代器对象,该对象包含Set对象中的 按插入顺序排列的所有元素的值 |
Set.prototype.entries() |
返回一个新的迭代器对象,该对象包含Set对象中的 按插入顺序排列的所有元素的值的[value, value]数组。为了使这个方法 和Map对象保持相似, 每个值的键和值相等 |
Set.prototype.forEach(callbackFn[, thisArg]) |
按照插入顺序,为Set对象中的每一个值调用一次callBackFn 。如果提供了thisArg参数,回调中的this会是这个参数 |
Set.prototype[@@interator]() |
返回一个新的Iterator 对象,它按插入顺序包含了Set对象中每个元素的 [key, value]**数组** |
(3)示例
使用Set对象
let set = new Set(); set.add(1); set.add("some text"); set.add("foo"); console.log(set.size); console.log(set.has(1)); set.delete("foo"); for (let item of set) { console.log(item); } //打印台信息: 3 true
迭代Set
//迭代 for (let item of set) console.log(item); for (let item of set.keys()) console.log(item); for (let item of set.values()) console.log(item); for (let [key, value] of set.entries()) console.log(key); // 用forEach迭代 set.forEach(function (value) { console.log(value); }); //打印台信息: 1 some text 1 some text 1 some text 1 some text 1 some text
数组和集合的转换
使用Array.from
或者展开操作符来完成集合到数组的转换。Set
的构造器接受数组作为参数,可以完成从Array
到Set
的转换。Set`对象中的值不重复,所以数组转换为集合时,所有重复值将会被删除。
console.log(Array.from(set));//集合转换为数组 console.log([...set]); //用...(展开操作符)操作符将Set转换为Array set = new Set([1, 2, 3, 4])//集合接收数组作为参数 console.log(set); //打印台信息: [ 1, 'some text' ] [ 1, 'some text' ] Set { 1, 2, 3, 4 }
String相关
const text = 'China'; set = new Set(text); console.log(set); console.log(set.size); //打印台信息: Set { 'C', 'h', 'i', 'n', 'a' } 5
数组去重
const num = [2, 3, 4, 4, 2, 4, 5]; console.log([...new Set(num)]); //打印台信息: [ 2, 3, 4, 5 ]
5、Array和Set的对比
在JavaScript中使用数组来存储一组元素,而新的集合对象有这些优势:
-
数组中用于判断元素是否存在的
indexOf
函数效率低下 -
Set
对象允许根据值删除元素,而数组中必须使用基于下标的 splice 方法 -
数组的
indexOf
方法无法找到NaN
值 -
Set
对象存储不重复的值,所以不需要手动处理包含重复值的情况 -