Javascript 可迭代对象是指实现了Symbol.iterator
方法的对象,该方法返回一个迭代器对象,可以通过迭代器对象来遍历对象中的元素。常见的可迭代对象包括数组、字符串、Map、Set等。可以使用for...of
循环或者使用迭代器的next
方法来遍历可迭代对象中的元素。
Symbol.iterator是一个内置的Symbol对象,它是用来定义一个对象的默认迭代器的。当一个对象被迭代时(比如使用for…of循环),它的Symbol.iterator方法会被调用,返回一个迭代器对象。这个迭代器对象具有next()方法,用于在对象中的元素上进行迭代。
通过实现Symbol.iterator方法,我们可以使一个对象成为可迭代的,从而可以使用for…of循环或者其他迭代器方法来遍历对象中的元素。常见的可迭代对象包括数组、字符串、Map、Set等。
下面是一个简单的示例,展示了如何使用Symbol.iterator来实现一个可迭代对象:
const days = {
start: new Date('2023-10-01'),
end: new Date('2023-10-05'),
[Symbol.iterator]() {
return {
current: this.start,
last: this.end,
next() {
if(this.current.getTime() <= this.last.getTime()) {
const value = this.current
this.current = new Date(this.current.getTime() + 24 * 3600 * 1000)
return {
done: false,
value
}
} else {
return {
done: true
}
}
}
}
}
}
for(let day of days) {
console.log(day)
}
为了让 days对象可迭代(也就让 for…of 可以运行)我们需要为对象添加一个名为 Symbol.iterator
的方法(一个专门用于使对象可迭代的内建 symbol)。
当 for…of 循环启动时,它会调用这个方法(如果没找到,就会报错)。这个方法必须返回一个 迭代器(iterator) —— 一个有 next 方法的对象
。
当 for…of 循环希望取得下一个数值,它就调用这个对象的 next() 方法。
next() 方法返回的结果的格式必须是 {done: Boolean, value: any},当 done=true 时,表示循环结束,否则 value 是下一个值。
在这里我们也可以结合yield
来实现:
const days = {
start: new Date('2023-10-01'),
end: new Date('2023-10-05'),
*[Symbol.iterator]() {
let current = this.start
let last = this.end
while(current.getTime() <= last.getTime()) {
value = current
current = new Date(current.getTime() + 24 * 3600 * 1000)
yield value
}
}
}
for(let day of days) {
console.log(day)
}
迭代对象的实现方式:
① 可迭代对象必须实现 Symbol.iterator 方法。objSymbol.iterator 的结果被称为 迭代器(iterator)。由它处理进一步的迭代过程。
②一个迭代器必须有 next() 方法,它返回一个 {done: Boolean, value: any} 对象,这里 done:true 表明迭代结束,否则 value 就是下一个值。
Symbol.iterator 方法会被 for…of 自动调用,但我们也可以直接调用它。
注:内建的可迭代对象例如字符串和数组,都实现了 Symbol.iterator。
在JavaScript中,yield
是ES6(ECMAScript 6)中的一种关键字,用于生成器函数中。通过yield
,我们可以在生成器函数中暂停执行并返回一个迭代器对象给调用者,再次调用时可以从上一次暂停的地方继续执行。
yield 的应用场景包括但不限于以下几个方面:
通过生成器函数和 yield 关键字,可以实现惰性求值,即只在需要时才计算值。这对于处理大量数据或者需要耗时计算的场景非常有用。
在 JavaScript 中,生成器函数中的 yield语句可以实现惰性求值。惰性求值是一种延迟计算的策略,即只在需要值的时候才进行计算。生成器函数的 yield语句可以用于实现这种惰性求值的特性。
下面是一个示例,演示了如何在生成器函数中使用 yield 实现惰性求值:
function* lazyEvaluation() {
let result1 = yield calculateResult1();
console.log(result1); // 这里会输出第一个结果
let result2 = yield calculateResult2();
console.log(result2); // 这里会输出第二个结果
}
function calculateResult1() {
console.log("Calculating result 1");
return 100;
}
function calculateResult2() {
console.log("Calculating result 2");
return 200;
}
let generator = lazyEvaluation();
// 调用 next() 时,并不会立即计算结果,而是在需要值的时候才进行计算
let next1 = generator.next(); // 这里会输出 "Calculating result 1"
let next2 = generator.next(); // 这里会输出第一个结果,然后输出 "Calculating result 2"
let next3 = generator.next(); // 这里会输出第二个结果
在上面的示例中,lazyEvaluation 是一个生成器函数,其中使用了 yield 语句来实现惰性求值。当调用生成器函数的 next() 方法时,计算并返回结果的操作被延迟到真正需要值的时候才进行。这样可以节省计算资源,并且使得程序更加高效。
yield 可以与异步操作结合使用,例如使用 Promise 对象。通过 yield 可以简化异步操作的编写,使代码更加清晰易懂。
在 JavaScript 中,生成器函数中的 yield 语句可以与异步编程结合使用,从而实现更加灵活和可读性更高的异步代码。通过结合生成器函数和异步操作,可以避免回调地狱(callback hell)和使用 Promise 的 then 方法时出现的嵌套问题。
下面是一个示例,演示了如何在生成器函数中使用 yield 语句实现异步编程:
function fakeAsyncAPI(value, delay) {
return new Promise(resolve => {
setTimeout(() => {
resolve(value);
}, delay);
});
}
function* asyncOperation() {
let result1 = yield fakeAsyncAPI('First result', 1000);
console.log(result1); // 输出: First result
let result2 = yield fakeAsyncAPI('Second result', 500);
console.log(result2); // 输出: Second result
}
function runAsyncOperation() {
let generator = asyncOperation();
function handleAsyncOperation(result) {
let next = generator.next(result);
if (next.done) {
return;
}
next.value.then(
res => {
handleAsyncOperation(res);
}
);
}
handleAsyncOperation();
}
runAsyncOperation();
生成器函数可以用于创建无限序列,例如斐波那契数列、无限递增的整数等。
在 JavaScript 中,可以使用生成器函数和 yield 语句来创建无限序列。生成器函数可以用于按需生成序列中的元素,而不需要提前将整个序列存储在内存中。
下面是一个示例,演示了如何使用生成器函数和 yield 语句创建一个无限序列:
function* infiniteSequence() {
let i = 0;
while (true) {
yield i;
i++;
}
}
let generator = infiniteSequence();
console.log(generator.next().value); // 输出: 0
console.log(generator.next().value); // 输出: 1
console.log(generator.next().value); // 输出: 2
// 以此类推,可以无限地获取序列中的下一个元素
yield 可以用于实现状态机,通过不同的 yield 值来表示不同的状态。
在 JavaScript 中,可以使用生成器函数和 yield 语句来实现状态机。状态机是一种用于描述对象在不同状态下的行为的数学模型,它包含有限个状态以及在这些状态之间的转换。
下面是一个简单的示例,演示了如何使用生成器函数和 yield 语句来实现一个简单的状态机:
function* stateMachine() {
let state = 'start';
while (true) {
let input = yield state;
if (input === 'go') {
state = 'running';
} else if (input === 'stop') {
state = 'stopped';
}
}
}
let machine = stateMachine();
console.log(machine.next().value); // 输出: start
console.log(machine.next('go').value); // 输出: running
console.log(machine.next('stop').value); // 输出: stopped
在上面的示例中,stateMachine 是一个生成器函数,它模拟了一个简单的状态机。每次调用生成器函数的 next() 方法时,会返回当前状态,并等待传入下一个输入。根据输入的不同,状态机会根据规则转换到不同的状态。
通过使用生成器函数和 yield 语句,我们可以很容易地实现状态机的逻辑。这种方式使得状态机的代码更加清晰和易于理解,同时也可以方便地实现状态之间的转换逻辑。
实现函数暂停和恢复的协程模式。
在 JavaScript 中,yield 关键字通常与生成器函数(Generator
Function)一起使用,用于实现协程(Coroutine)。协程是一种并发编程的模型,它允许在不同的执行上下文之间进行切换,并且可以保存和恢复执行状态。通过使用yield 和生成器函数,可以实现一种简单的协程模型。
在协程函数中,yield 可以暂停函数的执行,并且可以返回一个值给调用者。调用者可以在需要时恢复协程的执行,并且可以传入一个值作为协程函数中 yield 的结果。
下面是一个简单的示例,展示了如何使用生成器函数和 yield 实现协程函数:
function* coroutineFunction() {
let result = yield "First";
console.log(result);
result = yield "Second";
console.log(result);
}
let coroutine = coroutineFunction();
console.log(coroutine.next()); // 输出: { value: 'First', done: false }
console.log(coroutine.next("Hello")); // 输出: Hello
// 输出: { value: 'Second', done: false }
console.log(coroutine.next("World")); // 输出: World
// 输出: { value: undefined, done: true }
在这个示例中,coroutineFunction 是一个生成器函数,通过 yield 实现了协程的暂停和恢复。在调用 coroutine.next() 时,协程函数会在每个 yield 处暂停,并返回一个对象,包含当前的值和是否执行完毕的标志。调用 coroutine.next(value) 时,传入的值会作为上一个 yield 的结果,并且协程函数会继续执行直到下一个 yield 处。
这两个术语看起来差不多,但其实大不相同。
Iterable 如上所述,是实现了 Symbol.iterator
方法的对象。
Array-like 是有索引
和 length 属性
的对象,所以它们看起来很像数组。
① 一个可迭代对象也许不是类数组对象。
② 一个类数组对象可能不可迭代。比如:let arrayLike = { // 有索引和 length 属性 => 类数组对象 0: "Hello", 1: "World", length: 2 }; // Error (no Symbol.iterator) for (let item of arrayLike) {}
③ 一个对象,可能既是类数组对象,又是可迭代对象。比如,字符串既是可迭代的(for…of 对它们有效),又是类数组的(它们有数值索引和 length 属性)。