Commit f4a065ec authored by zhangfangli's avatar zhangfangli

map和object

parent 2bbf780c
Pipeline #15686 canceled with stage
---
title: 浅谈Proxy
date: 2022/3/29 12:05:17
tags:
- vue3
- proxy
author: 张芳利
---
# Map和Object
在 JavaScript 中,普通对象和 ES6 的新对象 Map 都可以存储键值对,平时普通对象用的较多,现在着重了解一下Map
<a name="AZnLW"></a>
## 描述
Map 对象存有键值对,其中的键可以是任何数据类型。<br />Map 对象记得键的原始插入顺序。<br />Map 对象具有表示映射大小的属性。
<a name="g2duN"></a>
## 创建
构造函数创建
```javascript
new Map([iterable])
```
Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, 'one' ],[ 2, 'two' ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。
<a name="d1eEZ"></a>
## 方法和属性
```javascript
let map = new Map()
```
打印map可得<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22328960/1650869031897-18b102f5-2bf1-40b1-ae59-50055cfeae95.png#clientId=u6819a0e1-7327-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=343&id=ubcd9b3cb&margin=%5Bobject%20Object%5D&name=image.png&originHeight=309&originWidth=481&originalType=binary&ratio=1&rotation=0&showTitle=false&size=21436&status=done&style=shadow&taskId=ubf631d28-9c30-4623-a110-07aaeb60424&title=&width=534.4444586023876)
```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
```
<a name="HHGjW"></a>
### size
访问属性,用于返回 一个[Map](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map) 对象的成员数量。
```javascript
let map = new Map([
['name','wawa'],
['age',18]
])
console.log(map.size);//2
```
<a name="gqCWw"></a>
### has()
返回一个bool值,用来表明map 中是否存在指定元素,返回布尔值
```javascript
let map = new Map([
['name','wawa'],
['age',18]
])
console.log(map.has('name'));// true
console.log(map.has('hahahah') //false
```
<a name="Jqcus"></a>
### 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'}
```
<a name="LXC1g"></a>
### get()
返回某个 Map 对象中的一个指定元素。
```javascript
let map = new Map([
['name','wawa'],
['age',18]
])
map.set('bar', 'foo');
console.log(map.get('name'));//wawa
```
<a name="EmioU"></a>
### 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
}
```
<a name="iIwd6"></a>
### 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);
}
```
<a name="DkShq"></a>
### 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** 对象的方法
<a name="C2l2a"></a>
### clear()
移除Map对象中的所有元素
```javascript
let map = new Map([
['name','wawa'],
['age',18]
])
map.set('bar', 'foo');
map.clear()
console.log(map.size);//0
```
<a name="bkyEw"></a>
### 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
```
<a name="mTQEz"></a>
### 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
```
<a name="rynQ5"></a>
### 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']
```
<a name="Nfw1C"></a>
### 利用散布运算符...遍历集合
```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']
```
<a name="KrSzR"></a>
## 和普通对象的区别
Objects 和 Maps 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。<br />不同的是:
<a name="McD5X"></a>
### 1. 默认值
1. Map 默认情况不包含任何键。只包含显式插入的键。
1. 一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
<a name="PK2H7"></a>
### 2. 键类型
1. 一个Object 的键必须是一个 [String](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String) 或是[Symbol](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol)
1. 一个 Map的键可以是任意值,包括函数、对象或任意基本类型。
<a name="UavZb"></a>
### 3. 键顺序
1. 一个 Object 的键是无序的
1. Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。
<a name="ei7Uf"></a>
### 4. Size
1. Object 的键值对个数只能手动计算
1. Map 的键值对个数可以轻易地通过[size](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map/size) 属性获取
<a name="qowlQ"></a>
### 5. 迭代
1. 迭代一个Object需要以某种方式获取它的键然后才能迭代
1. 使用 for...of 语句或 Map.prototype.forEach 直接迭代 Map 的属性
<a name="lmBoK"></a>
### 序列化和解析
1. 普通对象支持 JSON 序列化
1. Map 默认无法获取正确数据
<a name="ANmN0"></a>
### 性能
1. 在频繁增删键值对的场景下表现更好
1. 在频繁添加和删除键值对的场景下未作出优化
<a name="j3rp8"></a>
## 迭代协议:
迭代协议具体分为两个协议:可迭代协议和 迭代器协议。<br />**可迭代协议**:允许 JavaScript 对象定义或定制它们的迭代行为,例如,在一个 for..of 结构中,哪些值可以被遍历到。一些内置类型同时是内置可迭代对象,并且有默认的迭代行为,比如 Array 或者 Map,而其他内置类型则不是(比如 Object))<br />要成为**可迭代**对象, 一个对象必须实现 **@@iterator** 方法。这意味着对象(或者它[原型链](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 [Symbol.iterator](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) 访问该属性:
| 属性 | 值 |
| --- | --- |
| [Symbol.iterator] | 一个无参数的函数,其返回值为一个符合[迭代器协议](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#%E8%BF%AD%E4%BB%A3%E5%99%A8%E5%8D%8F%E8%AE%AE)的对象。 |
**迭代器协议:**定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值<br />只有实现了一个拥有以下语义(semantic)的 **next()** 方法,一个对象才能成为迭代器:
| 属性 | 值 |
| --- | --- |
| **next** | 一个无参数或者一个参数的函数,返回一个应当拥有以下两个属性的对象:<br />**done(boolean)**<br />如果迭代器可以产生序列中的下一个值,则为 false。<br />如果迭代器已将序列迭代完毕,则为 true。<br />这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。<br />**next()** 方法必须返回一个对象,<br />该对象应当有两个属性: done 和 value,如果返回了一个非对象值(比如 false 或 undefined),<br />则会抛出一个 [TypeError](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError) 异常("iterator.next() returned a non-object value")。 |
Iterator 迭代器是一种接口,为不同的数据结构提供统一的访问机制,这个访问机制主要是遍历,我们知道,在数组、在对象、在类数组、在map、在set里面,都可以用for of或者扩展运算符来得到一个数组或者是遍历当前的数据结构,为什么能够遍历的原因就是因为存在这个Iterator 迭代器这个东西,所以我们可以用for of来提供一个统一的遍历,因此这个底层或者是Itrator它最初是为了for of而设计的。
为了给所有的[数据结构](https://so.csdn.net/so/search?q=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84&spm=1001.2101.3001.7020)有一个统一的遍历接口,统一的访问机制,因此就产生了迭代器。<br />迭代器的特点:
- 按某种次序排列数据结构
- 为for of提供一个遍历支持
- 为不同的数据结构提供统一的访问机制(目的)
目前所有的内置可迭代对象如下:[String](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String)[Array](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array)[TypedArray](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)[Map](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map)[Set](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set)等,它们的原型对象都实现了 @@iterator 方法。对象(Object)之所以没有默认部署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。<br />例如:由上面的学习,我们知道map的values() 返回的就是一个Iterator对象
```javascript
let map = new Map([
['name','zz'],
['age',15]
])
console.log(map.values().next()); // {value: 'zz', done: false}
```
展开语法... :<br />其内部实现也使用了同样的迭代协议
```javascript
console.log(...'string') // s t r i n g
```
例如:[String](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String) 是一个内置的可迭代对象
```javascript
let str = 'string'
// 利用Symbol.iterator 属性获取一个迭代器对象
let it = str[Symbol.iterator]()
console.log(it); // StringIterator {}
console.log(it.next()); // {value: 's', done: false}
```
<a name="Wg0mh"></a>
### 问题:obj is not iterable
```javascript
let obj = {
name:'张小张',
age:18
}
console.log([...obj]); // Uncaught TypeError: obj is not iterable
```
怎么做好利用展开值且不报错??<br />根据[mdn](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#%E8%BF%AD%E4%BB%A3%E5%99%A8%E7%A4%BA%E4%BE%8B)迭代器的介绍:<br />给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]
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment