js迭代器与生成器
程序员文章站
2022-03-27 13:56:36
文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结前言es6规范新增了两个高级特性:迭代器和生成器,本节主要阐述迭代器提示:以下是本篇文章正文内容,下面案例可供参考一、pandas是什么?示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。二、使用步骤1.引入库代码如下(示例):import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimp....
前言
es6规范新增了两个高级特性:迭代器和生成器。迭代器的使用极大的简化了数据的操作, ECMAScript 较早的版本中,执行迭代必须使用循环或其他辅助结构。随着代码量增加,代码会变得越发混乱。很多语言都通过原生语言结构解决了这个问题,开发者无须事先知道如何迭代就能实现迭代操作。这个解决方案就是迭代器模式。Python、Java、C++,还有其他很多语言都对这个模式提供了完备的支持。JavaScript 在 ECMAScript 6 以后也支持了迭代器模式。
一、理解迭代
- 在 JavaScript 中,计数循环(for循环)就是一种最简单的迭代:
for (let i = 1; i <= 10; i++) {
console.log(i);
}
- 在迭代器出现之前,我们迭代对象一般是用迭代语句比如for循环,但是for循环有个弊端,它是通过递增索引,通过下标的方式访问数据,那么这在一定程度上限制了遍历的数据结构,我们在使用for循环的时候必须知道该遍历对象可以通过下标的方式访问值,比如数组和类数组(nodeList,arguments…)这种可以。但一般遍历的目的是为了操作里面的数据,它是什么数据解构我并不关心,我只想要里面的数据。而且总用for循环会显得代码臃肿,不利于维护。
- ES5 新增了 Array.prototype.forEach()方法,向通用迭代需求迈进了一步(但仍然不够理想): 这个方法解决了单独记录索引和通过数组对象取得值的问题。不过,没有办法标识迭代何时终止。因此这个方法只适用于数组,而且回调结构也比较笨拙。
let collection = ['foo', 'bar', 'baz'];
collection.forEach((item) => console.log(item));
// foo
// bar
// baz
一、迭代器
1.什么是迭代器
- 迭代器是一种特殊的对象,所有的迭代器对象都有一个next方法,调用该方法会返回一个结果对象,结果对象有两个属性value,done。迭代器对象还会保存一个内部指针,用来指向当前集合中的位置,迭代器每次调用Next方法都会返回一个可用的值。
- 下面用ES5的语法创建一个迭代器
let createIterator = function (data) {
//i是当前集合中的位置
let i = 0;
return {
next: function () {
let done = i >= data.length;
let value = done ? undefined : data[i++];
return {
done: done,
value: value
}
}
}
}
let iterator = createIterator([1, 2, 3]);
console.log(iterator.next());//{done: false, value: 1}
console.log(iterator.next());//{done: false, value: 2}
console.log(iterator.next());//{done: false, value: 3}
console.log(iterator.next());//{done: true, value: undefined}
//后面的都是
console.log(iterator.next());//{done: true, value: undefined}
2.可迭代协议
-实现 Iterable 接口(可迭代协议)要求同时具备两种能力:支持迭代的自我识别能力和创建实现Iterator 接口的对象的能力。在ECMAScirpt中,这意味着必须暴露一个属性作为“默认迭代器”,而且这个属性必须使用特殊的 Symbol.iterator 作为键。这个默认迭代器属性必须引用一个迭代器工厂函数(也可以叫生成器后面会提到),调用这个工厂函数必须返回一个新迭代器。
很多内置类型都实现了 Iterable 接口:
- 字符串
- 数组
- 映射
- 集合
- arguments
- NodeList等DOM集合类型
检查是否存在默认迭代器属性,可以通过"对象[Symbol.iterator]"的方式
// 这两种类型没有实现迭代器工厂函数
console.log(num[Symbol.iterator]); // undefined
console.log(obj[Symbol.iterator]); // undefined
let str = 'abc';
let arr = ['a', 'b', 'c'];
let map = new Map().set('a', 1).set('b', 2).set('c', 3);
let set = new Set().add('a').add('b').add('c');
let els = document.querySelectorAll('div');
// 这些类型都实现了迭代器工厂函数
console.log(str[Symbol.iterator]); // f values() { [native code] }
console.log(arr[Symbol.iterator]); // f values() { [native code] }
console.log(map[Symbol.iterator]); // f values() { [native code] }
console.log(set[Symbol.iterator]); // f values() { [native code] }
console.log(els[Symbol.iterator]); // f values() { [native code] }
// 调用这个工厂函数会生成一个迭代器
console.log(str[Symbol.iterator]()); // StringIterator {}
console.log(arr[Symbol.iterator]()); // ArrayIterator {}
console.log(map[Symbol.iterator]()); // MapIterator {}
console.log(set[Symbol.iterator]()); // SetIterator {}
console.log(els[Symbol.iterator]()); // ArrayIterator {}
在实际开发中我们不需要获取集合的迭代器来迭代对象,接收可迭代对象的原生语言特性包括:
- for-of 循环
- 数组解构
- 扩展操作符
- Array.from()
- 创建集合
- 创建映射
- Promise.all()接收由期约组成的可迭代对象
- Promise.race()接收由期约组成的可迭代对象
- yield*操作符,在生成器中使用
二、生成器
- 生成器是返回是一种返回迭代器的函数,它是函数。
- 生成器就是es6的Generator
- 生成器通过function后的*来表示,函数中会用到新的关键字yieId
- 生成器可以暂停函数执行,对异步编程意义重大
function* createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator.next());//{done: false, value: 1}
console.log(iterator.next());//{done: false, value: 2}
console.log(iterator.next());//{done: false, value: 3}
console.log(iterator.next());//{done: true,value: undefined}
- 生成器的一个常用功能是生成状态机
function* createIterator() {
while (true) {
yield A;
yield B;
yield C;
}
}
let iterator = createIterator();
console.log(iterator.next());//{done: false, value: A}
console.log(iterator.next());//{done: false, value: B}
console.log(iterator.next());//{done: false, value: C}
console.log(iterator.next());//{done: false, value: A}
console.log(iterator.next());//{done: false, value: B}
- 可以往next方法传值,值为上次yield的返回值,给第一个next传值,第一次调用next()方法时无论传入什么参数都会被丢弃。因为第一次执行Next,没有上一个yield。
function* createIterator() {
let one = yield 1;
let two = yield one + 2;
let three = yield two + 3;
}
let iterator = createIterator();
console.log(iterator.next(2));//{done: false, value: 1} 传入的参数2被忽略
console.log(iterator.next(4));//{done: false, value: 6} 传入的值是上一次yield的返回值也就是One等于4
console.log(iterator.next(8));//{done: false, value: 11} 同理
console.log(iterator.next());//{done: true, value: undefined}
console.log(iterator.next());//{done: true, value: undefined}
- 委托生成器
在某些时候我们需要将多个迭代器合成一个生成器,可以通过yield*的形式
function* createIterator1() {
yield 1;
yield 2;
yield 3;
}
function* createIterator2() {
yield 4;
yield 5;
}
function* createIterator() {
yield* createIterator1();
yield* createIterator2();
}
let iterator = createIterator();
console.log(iterator.next());//{value: 1, done: false}
console.log(iterator.next());//{value: 2, done: false}
console.log(iterator.next());//{value: 3, done: false}
console.log(iterator.next());//{value: 4, done: false}
console.log(iterator.next());//{value: 5, done: false}
console.log(iterator.next());//{value: undefined, done: true}
console.log(iterator.next());//{value: undefined, done: true}
- 使用 yield实现递归算法:yield最有用的地方是实现递归操作,此时生成器可以产生自身。看下面的例子:
function* nTimes(n) {
if (n > 0) {
yield* nTimes(n - 1);
yield n - 1;
}
}
for (const x of nTimes(3)) {
console.log(x);
}
// 0
// 1
// 2
三、探究
字符串
- 有没有想过为什么可以用for循环遍历字符串
因为字符串是有下标的,也就是说Js中的字符串跟数组很像,可以通过下标的方式去访问其中的每一个值,而for循环就是通过递增下标,去对应的数据结构通过下标访问其中的值,但这里不推荐用for循环遍历字符串,如图:
由于该生僻字没有识别出来,所以双字节字符被视作两个独立的编码单元,从而最终在A与B之间打印出2个�
所幸,ES6的目标是全面支持Unicode,并且我们可以通过改变字符串的默认迭代器来解决这个问题,使其操作字符而不是编码单元。现在,修改前一个示例中字符串的默认迭代器,让for-of循环输出正确的内容,因为js中的字符串默认实现了[Symbol.iterator]所以可以使用for of 想使用for of必须实现[Symbol.iterator]
前者一阵子改了一个bug也是字符集的事,添加了一个font-family才把生僻字显示出来 - 为什么通过扩展运算符字符串可直接转为数组?就是因为字符串默认实现了迭代器
对象
- 前面也提到过了,js中的对象是没有默认实现迭代器的,所以不能使用for of循环,那我们手动实现一下迭代器是不是就可以用了呢?答案是是的。
let obj = {
a: 1,
b: 2,
c: 10,
d: 100,
[Symbol.iterator]: function* () {
const arr = Object.keys(obj);
let len = arr.length;
let i = 0;
while (i < len) {
yield this[arr[i++]]
}
}
};
for (let item of obj) {
console.log(item);
}
obj.e = 1000;
obj.f = 100000;
for (let item of obj) {
console.log("第二次",item);
}
- 通过扩展运算符将对象转换成数组
let obj = {
a: 1,
b: 2,
c: 10,
d: 100,
[Symbol.iterator]: function* () {
const arr = Object.keys(obj);
let len = arr.length;
let i = 0;
while (i < len) {
yield this[arr[i++]];
}
}
};
let arrList = [...obj];
console.log("arrList", arrList);
总结
暂时想到这么多,后续可能会补充。
本文地址:https://blog.csdn.net/weixin_46773434/article/details/110433173
上一篇: Navicat 连接MySQL8.0.11出现2059错误
下一篇: 删除序列重复元素并保持顺序