胖蔡叨叨叨
你听我说

JS中的Map和Object的使用

在 JavaScript 中,普通对象和 ES6 的新对象 Map 都可以存储键值对,平时普通对象用的较多,现在着重了解一下Map

描述

Map 对象存有键值对,其中的键可以是任何数据类型。
Map 对象记得键的原始插入顺序。
Map 对象具有表示映射大小的属性。

创建

构造函数创建

javascript
new Map([iterable])

Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, ‘one’ ],[ 2, ‘two’ ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。

方法和属性

javascript
let map = new Map()

打印map可得
87263a741d1708d

javascript
Map(0) {size: 0}
  [[Entries]]
  No properties
  size: 0
  [[Prototype]]: Map
  clear: ƒ clear()
  constructor: ƒ Map()
  delete: ƒ delete()
  entries: ƒ entries()
  forEach: ƒ forEach()
  get: ƒ ()
  has: ƒ has()
  keys: ƒ keys()
  set: ƒ ()
  size: 0
  values: ƒ values()
  Symbol(Symbol.iterator): ƒ entries()
  Symbol(Symbol.toStringTag): "Map"
  get size: ƒ size()
  [[Prototype]]: Object

size

访问属性,用于返回 一个Map 对象的成员数量。

javascript
let map = new Map([
  ['name','wawa'],
  ['age',18]
])
console.log(map.size);//2 

has()

返回一个bool值,用来表明map 中是否存在指定元素,返回布尔值

javascript
let map = new Map([
  ['name','wawa'],
  ['age',18]
])

console.log(map.has('name'));// true
console.log(map.has('hahahah')  //false

set()

为 Map 对象添加或更新一个指定了键(key)和值(value)的(新)键值对

javascript
let map = new Map([
  ['name','wawa'],
  ['age',18]
])
map.set('bar', 'foo');
console.log(map);  //Map(3) {'name' => 'wawa', 'age' => 18, 'bar' => 'foo'}

get()

返回某个 Map 对象中的一个指定元素。

javascript
let map = new Map([
  ['name','wawa'],
  ['age',18]
])
map.set('bar', 'foo');
console.log(map.get('name'));//wawa

keys()

返回一个引用的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的key值

javascript
let map = new Map([
  ['name','wawa'],
  ['age',18]
])
map.set('bar', 'foo');
let keys = 	map.keys()
console.log(keys); //MapIterator {'name', 'age', 'bar'}

for(let k of keys){
  console.log(k);   // name  age bar
}

values()

返回一个新的Iterator对象。它包含按顺序插入Map对象中每个元素的value值。

javascript
let map = new Map([
  ['name','wawa'],
  ['age',18]
])
map.set('bar', 'foo');
let values = 	map.values()
console.log(values); //MapIterator {'wawa', 18, 'foo'}

for(let k of values){
  console.log(k);   
}

entries()

返回一个新的包含 [key, value] 对的 Iterator 对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同。

javascript
let map = new Map([
  ['name','wawa'],
  ['age',18]
])
map.set('bar', 'foo');
let entries = map.entries()
console.log(entries); //MapIterator {'name' => 'wawa', 'age' => 18, 'bar' => 'foo'}
console.log(entries.next().value); //(2) ['name', 'wawa']
console.log(entries.next().value); //(2)(2) ['age', 18]
console.log(entries.next().value); //(2) (2) ['bar', 'foo'] 
  • next() 是Iterator 对象的方法

    clear()

    移除Map对象中的所有元素

    javascript
    let map = new Map([
      ['name','wawa'],
      ['age',18]
    ])
    map.set('bar', 'foo');
    
    map.clear()
    console.log(map.size);//0

    delete()

    移除 Map 对象中指定的元素,返回布尔值

    javascript
    let map = new Map([
      ['name','wawa'],
      ['age',18]
    ])
    map.set('bar', 'foo');
    
    
    console.log(map.delete('name'));//true
    console.log(map.delete('namess'));//false

    forEach()

    按照插入顺序依次对 Map 中每个键/值对执行一次给定的函数

    javascript
    let map = new Map([
      ['name','wawa'],
      ['age',18]
    ])
    map.set('bar', 'foo');
    
    map.forEach((value,key,map)=>{
      console.log(`value:${value},key:${key}`);
    })
    //value:wawa,key:name  value:18,key:age value:foo,key:bar

    for…of…

    Map可以使用for..of循环来实现迭代

    javascript
    let map = new Map([
      ['name','wawa'],
      ['age',18]
    ])
    map.set('bar', 'foo');
    
    for(let m of map){
      console.log(m);
    }
    //(2) ['name', 'wawa'] ['age', 18]  ['bar', 'foo']

    利用散布运算符…遍历集合

    javascript
    let map = new Map([
      ['name','wawa'],
      ['age',18]
    ])
    map.set('bar', 'foo');
    let keys = 	map.keys()
    console.log(keys); //MapIterator {'name', 'age', 'bar'}
    
    console.log(...keys); // 'name', 'age', 'bar'
    console.log(...map)  // (2) ['name', 'wawa']['age', 18] ['bar', 'foo']

    和普通对象的区别

    Objects 和 Maps 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。
    不同的是:

    1. 默认值

    1. Map 默认情况不包含任何键。只包含显式插入的键。
    2. 一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

      2. 键类型

    3. 一个Object 的键必须是一个 String 或是Symbol
    4. 一个 Map的键可以是任意值,包括函数、对象或任意基本类型。

      3. 键顺序

    5. 一个 Object 的键是无序的
    6. Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。

      4. Size

    7. Object 的键值对个数只能手动计算
    8. Map 的键值对个数可以轻易地通过size 属性获取

      5. 迭代

    9. 迭代一个Object需要以某种方式获取它的键然后才能迭代
    10. 使用 for…of 语句或 Map.prototype.forEach 直接迭代 Map 的属性

      序列化和解析

    11. 普通对象支持 JSON 序列化
    12. Map 默认无法获取正确数据

      性能

    13. 在频繁增删键值对的场景下表现更好
    14. 在频繁添加和删除键值对的场景下未作出优化

迭代协议:

迭代协议具体分为两个协议:可迭代协议和 迭代器协议。
可迭代协议:允许 JavaScript 对象定义或定制它们的迭代行为,例如,在一个 for..of 结构中,哪些值可以被遍历到。一些内置类型同时是内置可迭代对象,并且有默认的迭代行为,比如 Array 或者 Map,而其他内置类型则不是(比如 Object))
要成为可迭代对象, 一个对象必须实现 @@iterator 方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性:

属性
[Symbol.iterator] 一个无参数的函数,其返回值为一个符合迭代器协议的对象。

迭代器协议:定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值
只有实现了一个拥有以下语义(semantic)的 next() 方法,一个对象才能成为迭代器:

属性
next 一个无参数或者一个参数的函数,返回一个应当拥有以下两个属性的对象:
done(boolean)
如果迭代器可以产生序列中的下一个值,则为 false。
如果迭代器已将序列迭代完毕,则为 true。
这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。
next() 方法必须返回一个对象,
该对象应当有两个属性: done 和 value,如果返回了一个非对象值(比如 false 或 undefined),
则会抛出一个 TypeError 异常(”iterator.next() returned a non-object value”)。

Iterator 迭代器是一种接口,为不同的数据结构提供统一的访问机制,这个访问机制主要是遍历,我们知道,在数组、在对象、在类数组、在map、在set里面,都可以用for of或者扩展运算符来得到一个数组或者是遍历当前的数据结构,为什么能够遍历的原因就是因为存在这个Iterator 迭代器这个东西,所以我们可以用for of来提供一个统一的遍历,因此这个底层或者是Itrator它最初是为了for of而设计的。

为了给所有的数据结构有一个统一的遍历接口,统一的访问机制,因此就产生了迭代器。
迭代器的特点:

  • 按某种次序排列数据结构
  • 为for of提供一个遍历支持
  • 为不同的数据结构提供统一的访问机制(目的)

目前所有的内置可迭代对象如下:StringArrayTypedArrayMap 和 Set等,它们的原型对象都实现了 @@iterator 方法。对象(Object)之所以没有默认部署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。
例如:由上面的学习,我们知道map的values() 返回的就是一个Iterator对象

javascript
let map = new Map([
      ['name','zz'],
      ['age',15]
    ])
  console.log(map.values().next()); // {value: 'zz', done: false}

展开语法… :
其内部实现也使用了同样的迭代协议

javascript
console.log(...'string') // s t r i n g

例如:String 是一个内置的可迭代对象

javascript
let str = 'string'
// 利用Symbol.iterator 属性获取一个迭代器对象
let it = str[Symbol.iterator]()
console.log(it); // StringIterator {}
console.log(it.next());  // {value: 's', done: false}

问题:obj is not iterable

javascript
let obj = {
    name:'张小张',
    age:18
  }
console.log([...obj]); // Uncaught TypeError: obj is not iterable

怎么做好利用展开值且不报错??
根据mdn迭代器的介绍:
给obj加上一个属性[Symbol.iterator],且返回next()

javascript
obj[Symbol.iterator] = function() {
    let nextIndex = 0;
    return {
        next: function () {
            let array = Object.values(obj)
            return nextIndex < array.length ? {
                value: array[nextIndex++],
                done: false
            } : {
                done: true
            };
        }
    };
}
			
console.log([...obj]) // ['张小张', 18]
赞(0) 打赏
转载请附上原文出处链接:胖蔡叨叨叨 » JS中的Map和Object的使用
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏