JavaScript------Map,Set详解

发布于:2022-11-28 ⋅ 阅读:(465) ⋅ 点赞:(0)

JavaScript------Map,Set详解

Map

ES6之前,在JavaScript中实现键值对 存储可以使用Object完成,使用对象属性作为键,使用属性作为引用值

ES6中新增Map,一种新的集合类型,为JavaScript这门语言带来真正的键值对 存储机制

创建映射

  • 创建空映射
  const m=new Map()
  • 使用嵌套数组初始化映射
  const m1=new Map([['key1','val1'],['key2','val2'],['key3','val3']])
  console.log(m1.size)//3
  • 映射期待的键值对,无论是否提供
  const m2=new Map([[]])
  console.log(m2.has(undefined))//true
  console.log(m2.get(undefined))//undefined

初始化映射之后

  1. get() 方法用来获取一个 Map 对象中指定的元素。
  2. set() 方法为Map对象添加一个指定键(key)和值(value)的新元素。
  3. has() 返回一个bool值,用来表明map 中是否存在指定元素。
  4. delete(key) 只删除一个键值对
  5. clear() 删除所有键值对
  const m3 = new Map([['aaa', '111'], ['bbb', '222'], ['ccc', '333']])
  console.log(m3.size)//3
  m3.set("ddd", 444)
  console.log(m3.keys()) // {'aaa', 'bbb', 'ccc', 'ddd'}
  console.log(m3.values()) //{'111', '222', '333', 444}
  console.log(m3.size) //4
  console.log(m3.has("ddd")) //true
  //传入键名
  console.log(m3.delete('ddd'))//true
  console.log(m3.size)//3
  //清空所有键值对
  m3.clear()
  console.log(m3.size)//0

set()方法返回映射实例,可以把多个操作连接起来,包括初始化声明

 const m4 = new Map().set('111', 'aaa')
  m4.set('222', 'bbb')
    .set('333', 'ccc')
  console.log(m4.size) //3

注意: 与Object只能使用数值,字符串或者符号作为键不同,Map可以使用任何JavaScript数据类型作为键

  const m = new Map()
  const functionKey = function () { }
  const symbolKey = Symbol()
  const objectKey = new Object()
  m.set(functionKey, "functionValue")
  m.set(symbolKey, "symbolValue")
  m.set(objectKey, "objectValue")
  console.log(m.get(functionKey))//functionValue
  console.log(m.get(symbolKey))//symbolValue
  console.log(m.get(objectKey))//objectValue

在映射中用作键和值的的对象及其他’集合’类型,在自己的内容或属性被修改时仍然保持不变

 const m = new Map()
  const objKey = {}, objVal = {}, arrKey = [], arrVal = [];
  m.set(objKey, objVal)
  m.set(arrKey, arrVal)
  objKey.foo = "foo"
  objVal.bar = "bar"
  arrKey.push("foo")
  arrVal.push('val')
  console.log(m.get(objKey))//{bar: 'bar'}
  console.log(m.get(arrKey))//['val']

顺序与迭代

  • Object类型的一个主要差异,Map实例会维护键值对的插入顺序,映射实例可以提供一个迭代器(Iterator),能以插入顺序生成[key,value]形式的数组,通过entries()方法(或者Symbol.iterator属性) 取得这个迭代器
 const m = new Map([['key1', 'val1'], ['key2', 'val2'], ['key3', 'val3']])

  console.log(m.entries === m[Symbol.iterator])//true
  for (let item of m.entries()) {
    console.log(item)
  }//['key1','val1'],['key2','val2'],['key3','val3']
  for (let item of m[Symbol.iterator]()) {
    console.log(item)
  }//['key1','val1'],['key2','val2'],['key3','val3']

  • entries()是默认迭代器,可以直接对映射实例使用扩展运算,把映射转换为数组
 console.log([...m]) //[ ['key1', 'val1'], ['key2', 'val2'],['key3', 'val3']]
  • 使用回调方式,调用映射的forEach()方法并传入回调
 m.forEach((val, key) => console.log(`${key}->${val}`))
  // key1->val1
  // key2->val2
  // key3->val3
  for(let e of m.keys()){
    console.log(e)
  } //key1 key2 key3
  for(let e of m.values()){
    console.log(e)
  } //val1 val2 val3
  • 键和值在迭代器遍历时是可以修改的,但映射内部的引用无法修改(可以修改作为键或值的对象内部的属性),键的字符串的原始值是不能修改的

    修改了作为键的对象的属性,但对象在映射内部仍然引用相同的值

  const keyObj={id:1}
  const m=new Map([[keyObj,'val1']])
  for(let key of m.keys())
  {
    key.id="newKey"
    console.log(key)//{id:"newKey"}
    console.log(m.get(keyObj));//val1
  }
  console.log(keyObj) //{id:"newKey"}

Map和Object

选择Map还是Object

对于多数 Web 开发任务来说,选择 Object 还是 Map 只是个人偏好问题,影响不大。不过,对于在乎内存和性能的开发者来说,对象和映射之间确实存在显著的差别。------《JavaScript高级程序设计》

  • 内存占用 ObjectMap 的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量.都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。不同浏览器的情况不同,但给定固定大小的内存,Map 大约可以比 Object 多存储 50%的键/值对。

  • 插入性能Object Map 中插入新键/值对的消耗大致相当,不过插入Map 在所有浏览器中一般会稍微快一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。如果代码涉及大量插入操作,那么显然 Map 的性能更佳。

  • 查找速度与插入不同,从大型 Object Map 中查找键/值对的性能差异极小,但如果只包含少量键/值对,则 Object 有时候速度更快。在把 Object 当成数组使用的情况下(比如使用连续整数作为属性),浏览器引擎可以进行优化,在内存中使用更高效的布局。这对 Map 来说是不可能的。对这两个类型而言,查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选择 Object 更好一些。

  • 删除性能使用 delete 删除 Object 属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此,出现了一些伪删除对象属性的操作,包括把属性值设置为 undefined null。但很多时候,这都是一种讨厌的或不适宜的折中。而对大多数浏览器引擎来说,Map delete()操作都比插入和查找更快。如果代码涉及大量删除操作,那么毫无疑问应该选择 Map

应用场景

  • Map

    1. 会频繁的更新和删除键值对时
    2. 存储大量数据时,尤其是key类型未知的情况下
    3. 需要频繁进行迭代处理
  • Object

    1. 仅做数据存储,并且属性仅为字符串或者Symbol
    2. 需要进行序列化转换为json传输时
    3. 当做一个对象的实例,需要保留自身的属性和方法时

Set

ES6新增Set是一种新集合类型,为这门语言带来了集合数据类型,在很多方面,Set像加强的Map

Set创建

  • 通过Set的构造函数创建Set(没有字面量创建的方式),可以先创建一个空的Set对象,随后使用add方法添加元素;

      const obj = { name: "aaa" }
      const set1 = new Set()
       set1.add(10)
      set1.add(10)
      set1.add(12)
      set1.add(14)
      set1.add(obj)
      console.log(set1)//Set(4) {10, 12, 14, {…}} 对象也是可以做元素的
    
  • 直接添加元素

    const set2 = new Set([10, 10, 12, 14, {name: "zzz"}])
    console.log(set2)
     console.log(set2)//Set(4) {10, 12, 14, {…}} 
    

    两种方法都创建了4个元素的Set 对象也是可以做元素的

Set常见属性,方法

  • Set常见的属性:size:返回Set中元素的个数

  • Set常用的方法:

    • add(value):添加某个元素,返回Set对象本身;
    const s = new Set();
    s.add(1).add(2).add(3);
    Array.from(s); // [1, 2, 3]
    
    
    • delete(value):从set中删除和这个值相等的元素,返回boolean类型;
    const s = new Set();
    s.add(1).add(2);
    s.delete(1);
    Array.from(s); // [2]
    
    • has(value):判断set中是否存在某个元素,返回boolean类型;
    const s = new Set();
    s.add(1).add(2).add(3);
    s.has(1); // true
    
    • clear():清空set中所有的元素,没有返回值;
    const s = new Set();
    s.add(1).add(2).add(3);
    Array.from(s); // [1, 2, 3]
    s.clear();
    Array.from(s); // []
    

迭代

  • forEach:通过forEach遍历set
const s = new Set();
s.add(1).add(2).add(3);
s.forEach((value, key) => console.log(key + ' : ' + value));
// 1 : 1
// 2 : 2
// 3 : 3
  • for of
const s = new Set();
s.add(1).add(2).add(3);
for (const i of s) {
	console.log(i);
}
// 1
// 2
// 3
  • 键名=键值
const s = new Set();
s.add(1).add(2).add(3);
Array.from(s.keys()); // [1, 2, 3]
Array.from(s.values()); // [1, 2, 3]
Array.from(s.entries()); // [[1, 1], [2, 2], [3, 3]]

Set去重

const arr = [1, 2, 3, 3, 4, 5, 4, 4, 2, 1, 3];
Array.from(new Set(arr)); // [1, 2, 3, 4, 5]
本文含有隐藏内容,请 开通VIP 后查看