JavaScript编码之路【ES6新特性之 Symbol 、Set 、Map、迭代器、生成器】(二)

简介: JavaScript编码之路【ES6新特性之 Symbol 、Set 、Map、迭代器、生成器】(二)

JavaScript编码之路【ES6新特性之 Symbol 、Set 、Map、迭代器、生成器】(一):https://developer.aliyun.com/article/1556700


三. Map映射

3.1. Map的基本使用

另外一个新增的数据结构是Map,用于存储映射关系。

但是我们可能会想,在之前我们可以使用对象来存储映射关系,他们有什么区别呢?

  • 事实上我们对象存储映射关系只能用字符串(ES6新增了Symbol)作为属性名(key)
  • 某些情况下我们可能希望通过其他类型作为key,比如对象,当尝试使用一个对象作为另一个对象的键时,JavaScript会将对象转换为字符串。通常情况下,这种转换会导致对象的类型信息和地址被转换为字符串。例如,[object Object]
const obj = {name: "why"}
const info = {
  [obj]: "kobe"
}
console.log(info) // { '[object Object]': 'kobe' }
const obj2 = {}
console.log(info[obj2]) // kobe

如上,const obj = { name: "why" } 创建了一个对象 obj,然后 const info = { [obj]: "kobe" } 创建了一个新的对象 info,它的键是 [object Object],值是"kobe"。而 const obj2 = {} 创建了另一个空对象 obj2,所以 info[obj2] 实际上是 info["[object Object]"],所以会打印出 kobe。

所以这显然不是我们想要的效果,那么我们就可以使用Map:

const obj1 = { name: "why" }
const obj2 = { age: 18 }

const map = new Map()
map.set(obj1, "abc")
map.set(obj2, "cba")
console.log(map.get(obj1))
console.log(map.get(obj2))

我们也可以在创建Map的时候,传入一个数组结构,数组结构中是一个个键值对的数组类型:

const map = new Map([
  [obj1, "abc"],
  [obj2, "cba"]
])
console.log(map.get(obj1))
console.log(map.get(obj2))

另外Map的key值也是不可以重复的:

const map = new Map([
  [obj1, "abc"],
  [obj2, "cba"],
  [obj1, "nba"]
])
console.log(map.get(obj1))
console.log(map.get(obj2))

3.2. Map的常见方法

Map常见的属性:

  • size:返回 Map 中元素的个数;

Map常见的方法:

  • set(key, value):在Map中添加 key 、value,并且返回整个Map对象
  • get(key):根据 key 获取 Map 中的 value
  • has(key):判断是否包括某一个 key,返回 Boolean 类型
  • delete(key):根据 key 删除一个键值对,返回 Boolean 类型
  • clear():清空所有的元素
  • forEach(callback, [, thisArg]):通过 forEach 遍历 Map
const obj = {}
map.set(obj, "mba")
map.get(obj)
map.has(obj)
// map.delete(obj)
// map.clear()

map.forEach((value, key, map) => {
  console.log(value, key, map)
})

Map 也可以通过 for...of 进行遍历:

for (const item of map) {
  console.log(item)
}

Map 也可以这样来遍历键值对

let myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);

// 遍历键
for (let key of myMap.keys()) {
  console.log(key);
}

// 遍历值
for (let value of myMap.values()) {
  console.log(value);
}

// 遍历键值对
for (let entry of myMap.entries()) {
  console.log(entry[0], entry[1]);
}

3.3. WeakMap使用

和Map类型的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的。

那么和Map有什么区别呢?

  • 区别一:WeakMap的key只能使用对象,不接受其他的类型作为key
  • 区别二:WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象
const weakMap = new WeakMap()
//  Invalid value used as weak map key
weakMap.set(1, "abc")
//  Invalid value used as weak map key
weakMap.set("aaa", "cba")

WeakMap常见的方法有四个:

  • set(key, value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value;
  • has(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;

注意:WeakMap也是不能遍历的

  • 因为没有forEach方法,也不支持通过for of的方式进行遍历
// TypeError: weakMap is not iterable
for (const item of weakMap) {
  console.log(item)
}

那么我们的WeakMap有什么作用呢?

class Dep {
  constructor() {
    this.subscribers = new Set();
  }

  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }

  notify() {
    this.subscribers.forEach(effect => {
      effect();
    })
  }
}

let activeEffect = null;
function watchEffect(effect) {
  activeEffect = effect;
  effect();
  activeEffect = null;
}


// Map({key: value}): key是一个字符串
// WeakMap({key(对象): value}): key是一个对象, 弱引用
const targetMap = new WeakMap();
function getDep(target, key) {
  // 1.根据对象(target)取出对应的Map对象
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }

  // 2.取出具体的dep对象
  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Dep();
    depsMap.set(key, dep);
  }
  return dep;
}


// vue3对raw进行数据劫持
function reactive(raw) {
  return new Proxy(raw, {
    get(target, key) {
      const dep = getDep(target, key);
      dep.depend();
      return target[key];
    },
    set(target, key, newValue) {
      const dep = getDep(target, key);
      target[key] = newValue;
      dep.notify();
    }
  })
}

上面的代码其实是通过WeakMap来收集响应式对象的依赖:

const obj1 = {
  name: "why",
  age: 18
}
const obj2 = {
  address: "北京市"
}

function nameFn1() {
  console.log("nameFn1")
}
function nameFn2() {
  console.log("nameFn2")
}

function ageFn1() {
  console.log("ageFn1")
}

function addressFn1() {
  console.log("addressFn1")
}

const obj1Map = new Map()
obj1Map.set("name", [nameFn1, nameFn2])
obj1Map.set("age", [ageFn1])
weakMap.set(obj1, obj1Map)

const obj2Map = new Map()
obj2Map.set("address", [addressFn1])
weakMap.set(obj2, obj2Map)


四、迭代器

迭代器是ES6引入的新特性,它不仅可以帮助我们访问元素集合中的每一个元素,而且还能记住我们访问的位置。就像图书管理员帮你梳理各种草稿,他不仅可以告诉你每张草稿上的内容,而且在你离开之后还能记住你读到哪一页。

咱们试着创建一本书,让迭代器来辅助咱们来阅读

const book = ['P1: Hello, world!', 'P2: Goodbye, world!']
const iterator = book[Symbol.iterator]()

【解释】


[Symbol.iterator]()是ES6引入的一个特殊的接口(叫做迭代协议)。实现这个接口的对象可以被for...of循环遍历,也可以作为扩展操作符的对象。

在JavaScript中,一些内置类型,如Array, String, Map, Set等默认已经实现了[Symbol.iterator]()方法。

所以book[Symbol.iterator]()就是调用book对象的Symbol.iterator方法,返回一个迭代器。这个迭代器可以用于遍历book中的每一项。

现在最有意思的部分来了。我们的迭代器有一本神奇的魔法书,每当你翻开这本书的时候,他就会指向下一个章节。

console.log(iterator.next()); // {value: "P1: Hello, world!", done: false}
console.log(iterator.next()); // {value: "P2: Goodbye, world!", done: false}
console.log(iterator.next()); // {value: undefined, done: true}

呼,看来我们已经读完了整本书。每次我们请求 next(),迭代器就会告诉我们新的内容,以及我们是否已经看完整本书(通过done属性)。


五、生成器

一个生成器就是一个特殊的函数,它可以在运行中被暂停和恢复,就像是你可以随时开始或停止冰淇淋机一样。在JavaScript中,生成器的定义方式是在函数前加一个星号(*),并且函数体内可以使用yield关键词来暂停函数。

假设我们有一个冰淇淋机器(生成器函数):

function* iceCreamGenerator() {
  yield 'vanilla';
  yield 'chocolate';
  yield 'strawberry';
}

现在,我们创建一个冰淇淋生成器(生成器对象)来制作冰淇淋:

const myIceCream = iceCreamGenerator()

每次你想要一个新口味,你只需请求一次:

console.log(myIceCream.next().value);  // 'vanilla'
console.log(myIceCream.next().value);  // 'chocolate'
console.log(myIceCream.next().value);  // 'strawberry'
console.log(myIceCream.next().done);   // true,哦豁,冰淇淋没有了


从生成器中得到冰淇淋的过程就像是在点冰淇淋一样,你可以随时停下来(通过yield),并在你准备好的时候再继续。最棒的是,当所有口味都用完时,你会收到一个消息,告诉你冰淇淋已经做完了(通过检查返回的对象的done属性)。


小结

目录
相关文章
|
3月前
|
JavaScript 前端开发 网络架构
ES6解构赋值详解;全面掌握:JavaScript解构赋值的终极指南
解构赋值是一个强大且灵活的工具,它使得从数组和对象中提取数据变得更加简单和直观。通过掌握解构赋值的基本语法和实践,你可以写出更加简洁、可读性更强的代码。在实际项目中,解构赋值广泛应用于函数参数处理、变量交换、嵌套结构操作等场景,极大地提升了代码的灵活性和简洁度。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
7月前
|
存储 JavaScript 前端开发
JS的ES6知识点
【10月更文挑战第19天】这只是 ES6 的一些主要知识点,ES6 还带来了许多其他的特性和改进,这些特性使得 JavaScript 更加现代化和强大,为开发者提供了更多的便利和灵活性。
172 58
|
7月前
|
JavaScript 前端开发 安全
ECMAScript 6(以下简称 ES6)的出现为 JavaScript 带来了许多新的特性和改进,其中 let 和 const 是两个非常重要的关键字。
ES6 引入了 `let` 和 `const` 关键字,为 JavaScript 的变量管理带来了革新。`let` 提供了块级作用域和暂存死区特性,避免变量污染,增强代码可读性和安全性;`const` 用于声明不可重新赋值的常量,但允许对象和数组的内部修改。两者在循环、函数内部及复杂项目中广泛应用,有助于实现不可变数据结构,提升代码质量。
97 5
|
8月前
|
JavaScript 前端开发 索引
JavaScript ES6及后续版本:新增的常用特性与亮点解析
JavaScript ES6及后续版本:新增的常用特性与亮点解析
269 4
|
7月前
|
前端开发 JavaScript
JavaScript新纪元:ES6+特性深度解析与实战应用
【10月更文挑战第29天】本文深入解析ES6+的核心特性,包括箭头函数、模板字符串、解构赋值、Promise、模块化和类等,结合实战应用,展示如何利用这些新特性编写更加高效和优雅的代码。
163 0
使用 entrySet 遍历 Map 类集合 KV
使用 entrySet 遍历 Map 类集合 KV
|
9月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
9月前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
10月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
10月前
|
Java
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。