Javascript 可迭代对象与yeild

2023-12-29 06:19:19

一、可迭代对象(Iterable object)

Javascript 可迭代对象是指实现了Symbol.iterator方法的对象,该方法返回一个迭代器对象,可以通过迭代器对象来遍历对象中的元素。常见的可迭代对象包括数组、字符串、Map、Set等。可以使用for...of循环或者使用迭代器的next方法来遍历可迭代对象中的元素。

二、Symbol.iterator

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。

三、yeild

在JavaScript中,yield是ES6(ECMAScript 6)中的一种关键字,用于生成器函数中。通过yield,我们可以在生成器函数中暂停执行并返回一个迭代器对象给调用者,再次调用时可以从上一次暂停的地方继续执行。

yield 的应用场景包括但不限于以下几个方面:

1.惰性求值

通过生成器函数和 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() 方法时,计算并返回结果的操作被延迟到真正需要值的时候才进行。这样可以节省计算资源,并且使得程序更加高效。

2.异步编程

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();

3.无限序列

生成器函数可以用于创建无限序列,例如斐波那契数列、无限递增的整数等。

在 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
// 以此类推,可以无限地获取序列中的下一个元素

4.状态机

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 语句,我们可以很容易地实现状态机的逻辑。这种方式使得状态机的代码更加清晰和易于理解,同时也可以方便地实现状态之间的转换逻辑。

5.协程函数

实现函数暂停和恢复的协程模式。

在 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)和类数组(array-like)

这两个术语看起来差不多,但其实大不相同。
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 属性)。

文章来源:https://blog.csdn.net/q1003675852/article/details/135247010
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。