2.14.迭代器
2.14.1.for…of
for...of
是 JavaScript ES6 引入的一种新的循环结构,,用于遍历可迭代对象(Iterable objects)的每个元素。
它可以自动调用目标对象的迭代器接口,并按顺序逐个访问集合中的每个值。
它可以用来遍历数组、Set、Map、某种类型的字符串、生成器对象(Generator object)以及其他实现了可迭代协议的对象。
2.14.1.1.用法
// 遍历数组
let numbers = [1, 2, 3, 4, 5];
for (let num of numbers) {
console.log(num);
}
// 输出:
// 1
// 2
// 3
// 4
// 5
let names = [ '王小二', '李小三', '赵小四', '赵小四' ];
for (let name of names) {
console.log(name);
}
// 输出:
// 王小二
// 李小三
// 赵小四
// 赵小四
// 遍历字符串
let str = 'hello';
for (let char of str) {
console.log(char);
}
// 输出:
// 'h'
// 'e'
// 'l'
// 'l'
// 'o'
// 遍历 Set
let set = new Set([ '王小二', '李小三', '赵小四' ]);
for (let name of set) {
console.log(name);
}
// 输出:
// 王小二
// 李小三
// 赵小四
// 遍历 Map
let map = new Map([['name', '王小二'], ['age', 30]]);
for (let [key, value] of map) {
console.log(`Key: ${key}, Value: ${value}`);
}
// 输出:
// Key: name, Value: John
// Key: age, Value: 30
通过 for...of
循环,开发者可以更加简洁、直观地遍历多种数据结构
2.14.1.2.和 for...in
比较
for...of
和 for...in
都是 JavaScript 中的循环结构,但它们在用途和遍历对象的方式上有所区别:
const names = ['王小二','李不三','赵小四','刘小六'];
for(let i in names){
console.log(i, names[i])
}
for(let n of names){
console.log(n);
}
for…of :
- 用于遍历可迭代对象(Iterable objects),如数组、字符串、Set、Map、生成器函数(generator function)产生的序列等。
- 它会直接遍历并返回数据结构中的实际值或内容。
- 示例:
let arr = [1, 2, 3];
for (let value of arr) {
console.log(value); // 输出:1, 2, 3
}
for…in:
- 用于遍历对象(Object)自身的可枚举属性(不包括继承自原型链的属性)。
- 它遍历的是对象的键名(key),而非键值。
- 对于数组,使用
for...in
循环可以遍历到非数字索引的属性,这通常不是期望的行为,除非特别处理。 - 示例:
let obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
console.log(key); // 输出:"a", "b", "c"
console.log(obj[key]); // 输出:1, 2, 3
}
总结:
- 如果需要遍历数组或其它可迭代对象的实际元素值,应使用
for...of
。 - 如果要遍历对象的所有自有可枚举属性名,则使用
for...in
。但在遍历数组时,更推荐使用for...of
或者数组的forEach()
方法来避免意外包含非数值索引的属性。如果确实需要同时处理数组和其他对象属性,可能需要结合使用for...of
和Object.keys()
等方法。
2.14.2.迭代器
在 ECMAScript 6(ES6)中,迭代器(Iterator)是一种接口,它为不同的数据结构提供了一种统一的方式来遍历和访问集合中的元素。
迭代器是 JavaScript 中用于顺序访问集合内项目的一种机制。
2.14.2.1.说明
-
Symbol.iterator:为了能够被迭代器遍历,一个对象需要在其自身上部署
Symbol.iterator
属性, 这个属性的值是一个函数,被称为迭代器生成器,每次调用该函数都会返回一个新的迭代器对象。
-
调用函数创建一个指针对象,指向当前数据结构的起始位置
第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
-
调用next()方法会返回一个包含两个属性的对象:
value
和done
。-
value
属性表示当前迭代位置的值。 -
done
属性是一个布尔值,如果为true
表示迭代已结束,没有更多的元素可以迭代;若为
false
,则表示还有更多值可以获取,并且value
就是下一个要访问的值。
-
// Array
const names = ['王小二','李不三','赵小四','刘小六'];
let iterator = names[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next()); // {value: "王小二", done: false}
console.log(iterator.next()); // {value: "李不三", done: false}
console.log(iterator.next()); // {value: "赵小四", done: false}
console.log(iterator.next()); // {value: "刘小六", done: false}
console.log(iterator.next()); // {value: undefined, done: true}
// Map
let map2 = new Map([
['name', '王小二'],
[123, 'abc'],
[{ key: 'objectKey' }, 'Value for object key']
]);
let iterator = map2[Symbol.iterator]();
console.log(iterator.next()); // {value: ['name', '王小二'], done: false}
console.log(iterator.next()); // {value: [123, 'abc'], done: false}
console.log(iterator.next()); // {value: [{…}, 'Value for object key'] , done: false}
console.log(iterator.next()); // {value: undefined, done: true}
通过迭代器,开发者可以编写更加通用的算法,这些算法无需关心底层数据结构的具体实现,只要数据结构提供了迭代器接口即可遍历其内容。
同时,结合 for...of
循环和其他相关特性(如解构赋值),迭代器极大地简化了遍历操作的代码编写。
2.14.2.2.支持的类型
原生支持迭代器接口的数据类型包括但不限于:
- Array
- String
- TypedArray
- Map
- Set
- NodeList 等
2.14.2.3.自定义迭代器
定义对象, 但在调用 for…of 时 , 遍历对象中的数组
通常可以使用 friends.fs.forEach() , 但这样 直接暴露了 fs属性
通过 定义Symbol.iterator属性 返回对象
//声明一个对象
const friends = {
name: "我的好朋友",
fs: [
'王小二','李小三','赵小四','刘小六'
],
[Symbol.iterator]() {
//索引变量
let index = 0;
//
let _this = this;
return {
next: function () {
if (index < _this.fs.length) {
const result = { value: _this.fs[index], done: false };
//下标自增
index++;
//返回结果
return result;
}else{
return {value: undefined, done: true};
}
}
};
}
}
//遍历这个对象
for (let v of friends) {
console.log(v);
}
2.15. 生成器
在 ECMAScript 6 (ES6) 中,生成器(Generators)是一种特殊的迭代器生成函数,可以暂停执行并保存其上下文状态,之后可以恢复执行。
生成器通过 function*
关键字定义,
并使用 yield
表达式来产出值或表达式结果。
以下是一个简单的 ES6 生成器示例:
function* simpleGenerator() {
yield 'one ';
yield 'two';
yield 'three';
}
let generator = simpleGenerator();
console.log(generator.next()); // 输出 {value: 'one ', done: false}
console.log(generator.next()); // 输出 {value: "two", done: false}
console.log(generator.next()); // 输出 {value: "three", done: false}
console.log(generator.next()); // 输出 {value: undefined, done: true} 因为已经没有更多的yield语句了
// 或者一次性获取所有产出值
for (let value of simpleGenerator()) {
console.log(value);
}
/*
输出 :
one
two
three
*/
在上述代码中,simpleGenerator()
是一个生成器函数,每次调用 next()
方法时,它都会从上次暂停的地方继续执行到下一个 yield
表达式,
并返回一个包含 { value: ..., done: ... }
的迭代器结果对象。当没有更多 yield
表达式时,done
属性将为 true
,表示生成器已完成执行。
生成器在处理异步编程、遍历复杂数据结构以及创建迭代器等方面非常有用。
2.15.1.可以理解为程序的分隔符
function* simpleGenerator() {
console.log("第一部分")
yield 'one ';
console.log("第二部分")
yield 'two';
console.log("第三部分")
yield 'three';
console.log("第四部分")
}
let generator = simpleGenerator();
// 第一部分
console.log(generator.next()); // 输出 {value: 'one ', done: false}
// 第二部分
console.log(generator.next()); // 输出 {value: "two", done: false}
// 第三部分
console.log(generator.next()); // 输出 {value: "three", done: false}
// 第四部分
console.log(generator.next()); // 输出 {value: undefined, done: true} 因为已经没有更多的yield语句了
//
for (let value of simpleGenerator()) {
console.log(value);
}
/*
输出 :
第一部分
one
第二部分
two
第三部分
three
第四部分
*/
2.15.2.next() 传参
function* simpleGenerator( arg ) {
console.log("第一部分 : " + arg )
let aaa = yield 'one ';
console.log("第二部分 : " + aaa )
let bbb = yield 'two';
console.log("第三部分 : " + bbb )
let ccc = yield 'three';
console.log("第四部分 : " + ccc )
}
let generator = simpleGenerator('AAA');
// 第一部分 : AAA
console.log(generator.next()); // 输出 {value: 'one ', done: false}
//next方法可以传入实参 // 第二部分 : BBB
console.log(generator.next('BBB')); // 输出 {value: "two", done: false}
// 第三部分 : CCC
console.log(generator.next('CCC')); // 输出 {value: "three", done: false}
// 第四部分 : DDD
console.log(generator.next('DDD')); // 输出 {value: undefined, done: true} 因为已经没有更多的yield语句了
2.15.3.解决回调嵌套
// 异步编程 文件操作 网络操作(ajax, request) 数据库操作
// 这里使用 setTimeout 函数模拟异步操作
// 1s 后控制台输出 111 2s后输出 222 3s后输出 333
// 回调地狱(回调函数嵌套)
// setTimeout(() => {
// console.log(111);
// setTimeout(() => {
// console.log(222);
// setTimeout(() => {
// console.log(333);
// }, 3000);
// }, 2000);
// }, 1000);
function one(){
setTimeout(()=>{
console.log(111);
iterator.next();
},1000)
}
function two(){
setTimeout(()=>{
console.log(222);
iterator.next();
},2000)
}
function three(){
setTimeout(()=>{
console.log(333);
iterator.next();
},3000)
}
function * simpleGenerator(){
yield one();
yield two();
yield three();
}
//调用生成器函数
let iterator = simpleGenerator();
iterator.next();
2.15.4.回调传参
//模拟获取 用户数据 订单数据 商品数据
function getUsers(){
setTimeout(()=>{
let data = '用户数据';
//调用 next 方法, 并且将数据传入
iterator.next(data);
}, 1000);
}
function getOrders(){
setTimeout(()=>{
let data = '订单数据';
iterator.next(data);
}, 1000)
}
function getGoods(){
setTimeout(()=>{
let data = '商品数据';
iterator.next(data);
}, 1000)
}
function * simpleGenerator(){
console.log('开始获取数据');
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
//调用生成器函数
let iterator = simpleGenerator();
iterator.next();