迭代器和生成器
迭代器
在JavaScript中迭代器是一个对象,它是一个使用了next()
方法实现了迭代器协议的的对象(方法名是约定的,必须是next,不能是其他的)。JavaScript中可以使用迭代器的常见对象有Array、Map、Set、String
。我们可以通过Symbol.iterator
属性获取当前实例的迭代器对象,如果存在的话。
这个next()方法每次返回一个对象,其中包含两个值:
value:表示当前迭代返回对象的值
done:表示迭代是否完成
const arr = [true, 2, 3, 4, 5];
const iter = arr[Symbol.iterator]();
console.log(typeof iter.next().value);
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());// 输出undefined和true,表示数组已经迭代完毕
迭代器一旦创建就可以显示的调用next方法完成迭代。迭代一个迭代器被称为消耗了这个迭代器,因为它通常只能执行一次。这意味着,每执行一次for…of都会创建一个新的迭代器对象
。
下面我们实现一个简单的迭代器。
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
let nextIndex = start; // 外部变量
let iterationCount = 0;// 外部变量
const rangeIterator = {
a() {
let result;
if (nextIndex < end) {
result = { value: nextIndex, done: false };
nextIndex += step;
iterationCount++;
return result;
}
return { value: undefined, done: true };
},
};
return rangeIterator;
}
const iter = makeRangeIterator(1, 5, 1);
console.log(iter.a());// { value: 1, done: false }
console.log(iter.a());// { value: 2, done: false }
console.log(iter.a());// { value: 3, done: false }
console.log(iter.a());// { value: 4, done: false }
console.log(iter.a());// { value: undefined, done: true }
疑问点
:
有人可能会问不是说代表next()方法不能变吗,为什么这里方法名是a,却能正常运行。这是因为这是手动迭代,要是使用for…of就会报错,因为for...of只认可名为next()的方法
。
还有人可能会疑问为什么nextIndex和iterationCount这两个变量能够一直被a方法访问,它们不应该在const iter = makeRangeIterator(1, 5, 1);
这段代码执行完毕就不能被访问了吗,这里涉及到JavaScript的另外一个高级特性——闭包
,不了解的伙伴可以移步本栏目下关于JavaScript的函数,了解这一特性。
可迭代对象
JavaScript中的可迭代对象要求对象本身或者对象的原型链
实现了名为Symbol.iterator
方法,且该方法返回一个迭代器对象
。
我们可以通过Symbol.iterator属性去获取实现了迭代器的对象的迭代器对象。
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const iter = arr[Symbol.iterator]();
let done = false;
do {
const { value, done: Done } = iter.next();
done = Done;
console.log(value);
} while (!done);
在以上代码中,我们获取了arr的迭代器对象,并完成了对arr的手动迭代。
下面我们手动实现一个可迭代对象。我们创建一个名为makeRangeIterator的函数,它会返回一个名为rangeIterator的可迭代对象,这个对象使用括号语法成功拥有了名为Symbol.iterator
的方法。而这个方法又返回了一个实现了next()方法的迭代器对象
。最后我们使用for…of完成了迭代。
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
let iterationCount = 0;
const rangeIterator = {
[Symbol.iterator]() {
return {
next() {
let result;
if (nextIndex < end) {
result = { value: nextIndex, done: false };
nextIndex += step;
iterationCount++;
return result;
}
return { value: undefined, done: true };
},
};
},
};
return rangeIterator;
}
const iter = makeRangeIterator(1, 5, 1);
// 使用 for...of 循环
for (const value of iter) {
console.log(value); // 1, 2, 3, 4
}
一些语句和表达式专用于可迭代对象,例如 for…of 循环、展开语法、yield* 和解构语法。
for (let value of ["a", "b", "c"]) {
console.log(value);
}
// "a"
// "b"
// "c"
[..."abc"]; // ["a", "b", "c"]
function* gen() {
yield* ["a", "b", "c"];
}
gen().next(); // { value: "a", done: false }
[a, b, c] = new Set(["a", "b", "c"]);
a; // "a"
生成器
生成器是一个函数,它允许你定义一个非连续执行的函数
作为迭代算法。由function *语法和yield关键字组成的函数就是生成器函数。调用生成器函数之后会返回一个Generator对象,这个对象是实现了迭代器协议和可迭代协议的迭代器对象。
function *
:用于定义生成器函数。当调用生成器函数时,它不会立即执行函数体内的代码,而是返回一个 Generator 对象。可以通过 next() 方法逐步执行生成器函数体内的代码。
yield
:
- 暂停执行:yield表达式用于在生成器函数体内
暂停执行,并返回一个值
。每次调用 next()方法时,生成器函数会从上次暂停的地方继续执行
,直到遇到下一个 yield表达式或函数结束(return值)
。 - 返回值和状态:表达式返回一个对象,包含两个属性:
value
:当前迭代的值。done
:布尔值,表示迭代是否完成。
function* makeRangeGenerator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
while (nextIndex < end) {
yield nextIndex;
console.log("通过yield的暂停");
nextIndex += step;
}
}
const gen = makeRangeGenerator(1, 5, 1);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false } 通过yield的暂停
console.log(gen.next()); // { value: 3, done: false } 通过yield的暂停
console.log(gen.next()); // { value: 4, done: false } 通过yield的暂停
console.log(gen.next()); // { value: undefined, done: true } 通过yield的暂停
yield除了能够返回一个对象,还能接收Generator对象调用next()方法时传递的参数。
function* makeRangeGenerator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
while (nextIndex < end) {
let increment = yield nextIndex;
console.log("通过yield的暂停,接收到的参数:", increment);
if (increment !== undefined) {
step = increment;
} else {
step = 1;
}
nextIndex += step;
}
}
const gen = makeRangeGenerator(1, 20, 1);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next(2)); // { value: 3, done: false } (1 + 2)
console.log(gen.next()); // { value: 4, done: false } (3 + 1)
console.log(gen.next()); // { value: undefined, done: true } (4 + 1 >= 5)
function* makeRangeGenerator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
while (nextIndex < end) {
let increment = yield nextIndex;
console.log("通过yield的暂停,接收到的参数:", increment);
if (increment === 4) {
console.log("你传入了禁忌参数,导致生成器提前中止", increment);
return;
} else if (increment !== undefined) {
step = increment;
} else {
step = 1;
}
nextIndex += step;
}
}
const gen = makeRangeGenerator(1, 20, 1);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next(2)); // { value: 3, done: false } (1 + 2)
console.log(gen.next(4)); // { value: undefined, done: true } 提前中止
生成器在 JavaScript 中有多种使用场景,它们提供了一种强大的方式来处理异步操作、生成大量数据、以及实现复杂的迭代逻辑。
标签:console,log,迭代,JavaScript,生成器,value,next,done From: https://blog.csdn.net/weixin_62652659/article/details/143829201