javascript中的生成器和迭代器

生成器函数和迭代器是 JavaScript 中非常有用的工具,它们能够帮助我们轻松地遍历集合数据类型,使代码更加简洁、清晰。他们都是用于处理集合数据类型的工具,它们可以帮助我们迭代集合中的元素,并执行相应的操作。

迭代器

JavaScript中的迭代器是一个对象,它提供了一个统一的接口来遍历集合中的元素,而不需要了解集合的内部实现。通过使用迭代器,我们可以对集合中的元素进行循环处理,每次处理一个元素,直到处理完整个集合为止。

具体来说,一个迭代器对象需要实现一个next()方法,该方法返回一个对象,包含两个属性:valuedonevalue属性包含当前迭代的元素的值,而done属性则是一个布尔值,表示是否已经迭代完整个集合。当迭代完整个集合时,done属性为true,否则为false

JavaScript中的数组、Map、Set等集合数据类型都实现了迭代器接口,可以通过调用其内置的Symbol.iterator方法获取迭代器对象。 下面是一个使用迭代器遍历数组的例子:

const arr = [1, 2, 3, 4, 5];
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {console.log(result.value);result = iterator.next();
}
// 输出1 2 3 4 5 

生成器

生成器是一种特殊的函数,它可以在执行过程中暂停,并返回一个迭代器对象。生成器函数通过function*语法来定义,在函数体内使用yield语句可以暂停函数执行,并将值返回给调用方。调用方可以通过迭代器对象来恢复生成器函数的执行,并在下一个yield语句处继续执行。

生成器函数返回的迭代器对象和普通迭代器对象类似,都有一个next()方法,可以用来获取生成器函数中使用yield语句返回的值。但是,生成器函数可以在执行过程中多次返回值,并且可以在每次返回值之间执行一些逻辑操作,这使得生成器函数比普通迭代器更加灵活。

下面是一个使用生成器函数生成斐波那契数列的例子:

function* fibonacci() {let [prev, curr] = [0, 1];while (true) {[prev, curr] = [curr, prev + curr];yield curr;}
}

const fib = fibonacci();
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5
console.log(fib.next().value); // 8 

在上面的例子中,fibonacci函数是一个生成器函数,它会生成一个斐波那契数列。在函数体内部,使用了while(true)循环来生成数列中的每一项。在每次循环中,更新prevcurr变量的值,然后使用yield语句返回当前项的值。这个函数可以无限地生成数列,因为它没有终止条件。

在调用fibonacci函数之后,将返回一个迭代器对象fib。我们可以使用next()方法来逐一获取数列中的每一项,并将其打印出来。

在第一次调用fib.next().value时,会执行fibonacci函数中的代码,生成数列中的第一项(值为1),然后暂停函数的执行,并将该值返回给调用方。在第二次调用fib.next().value时,会继续执行fibonacci函数中的代码,生成数列中的第二项(值为2),然后再次暂停函数的执行,并将该值返回给调用方。以此类推,每次调用next()方法,都会从上一次暂停的位置继续执行生成器函数,并生成下一项的值。

我们再来看几个例子。

1.处理数据集合

使用迭代器可以方便地遍历数据集合,而生成器可以生成一个可迭代的对象,从而更加方便地处理数据集合。例如,我们可以使用生成器来生成一个无限序列:

javascriptCopy code
function* fibonacci() {let a = 0, b = 1;while (true) {yield a;[a, b] = [b, a + b];}
}

const fib = fibonacci();
for (let i = 0; i < 10; i++) {console.log(fib.next().value);
} 

在上面的示例中,我们定义了一个 fibonacci 生成器函数,该函数可以生成一个斐波那契数列。通过使用迭代器,我们可以遍历该数列的前 10 项。

2.实现异步编程

在 JavaScript 中,生成器可以用来实现异步编程,从而避免回调地狱。通过使用 yield 关键字,我们可以将异步操作挂起,等待异步操作完成后再继续执行。

例如,我们可以使用生成器函数来实现异步读取文件的操作:

function readFile(filename) {return new Promise((resolve, reject) => {fs.readFile(filename, (err, data) => {if (err) reject(err);else resolve(data);});});
}

function* readFiles() {const file1 = yield readFile('file1.txt');const file2 = yield readFile('file2.txt');console.log(file1.toString());console.log(file2.toString());
}

const iterator = readFiles();
iterator.next().value.then(data => iterator.next(data).value).then(data => iterator.next(data)); 

在上面的示例中,我们定义了一个 readFiles 生成器函数,该函数可以异步读取两个文件的内容,并输出到控制台上。通过使用迭代器和 Promise,我们可以方便地控制异步操作的执行顺序。

3.使用迭代器和生成器实现分帧加载大量的 DOM 节点,从而提高页面的性能和响应速度。下面是一个实现分帧加载的示例代码:

function* generateNodes(num) {for (let i = 0; i < num; i++) {yield createNode(i);}
}

function createNode(i) {const node = document.createElement('div');node.textContent = `Node ${i}`;return node;
}

function appendNodes(container, num, interval = 16) {const iterator = generateNodes(num);let count = 0;const handle = setInterval(() => {const { value, done } = iterator.next();if (done) {clearInterval(handle);return;}container.appendChild(value);count++;if (count === num) {clearInterval(handle);}}, interval);
}

const container = document.getElementById('container');
appendNodes(container, 100000); 

在上面的代码中,我们首先定义了一个生成器函数 generateNodes,它接受一个数字参数 num,用于生成指定数量的 DOM 节点。在生成器函数中,我们通过 for 循环来生成每个节点,并使用 yield 关键字将节点返回。

接下来,我们定义了一个辅助函数 createNode,用于创建单个 DOM 节点。在这个函数中,我们使用 document.createElement 方法创建一个新的 div 元素,并将节点的文本内容设置为 Node ${i},其中 i 是节点的索引。

最后,我们定义了一个函数 appendNodes,它接受三个参数:container 表示要将节点添加到的容器元素,num 表示要生成的节点数量,interval 表示分帧加载的时间间隔,默认为 16ms。在函数中,我们首先调用 generateNodes 函数创建一个迭代器,然后使用 setInterval 方法来定时添加节点。在每次定时器回调函数中,我们通过迭代器的 next 方法获取下一个节点,并将节点添加到容器中。当添加完指定数量的节点后,我们清除定时器,并结束函数的执行。

通过以上代码,我们可以将大量的 DOM 节点分帧加载到页面中,避免页面卡顿和响应缓慢的问题。同时,由于采用了迭代器和生成器的方式,代码也更加简洁和易于维护。

总之,生成器和迭代器是 JavaScript 中非常有用的概念,它们可以帮助我们更加方便地处理数据集合、实现异步编程等场景。

4.generator实现状态机,在 JavaScript 中,可以使用生成器实现状态机,这样可以简化状态机的实现和维护。下面是一个使用生成器实现状态机的示例代码:

function* stateMachine() {let state = 'INITIAL';while (true) {const input = yield state;switch (state) {case 'INITIAL':if (input === 'start') {state = 'RUNNING';}break;case 'RUNNING':if (input === 'pause') {state = 'PAUSED';} else if (input === 'stop') {state = 'STOPPED';}break;case 'PAUSED':if (input === 'resume') {state = 'RUNNING';} else if (input === 'stop') {state = 'STOPPED';}break;case 'STOPPED':return;}}
}

const machine = stateMachine();

console.log(machine.next().value); // INITIAL
console.log(machine.next('start').value); // RUNNING
console.log(machine.next('pause').value); // PAUSED
console.log(machine.next('resume').value); // RUNNING
console.log(machine.next('stop').value); // STOPPED 

在上面的代码中,我们定义了一个生成器函数 stateMachine,它表示一个状态机。在状态机中,我们定义了一个状态变量 state,并使用 while 循环和 yield 关键字来构建状态机的迭代器。

在每次迭代中,我们首先使用 yield 关键字将当前状态返回,然后通过 yield 关键字接收输入值 input。根据当前状态和输入值,我们使用 switch 语句来判断状态机的转移逻辑,并更新状态变量 state

最后,我们通过调用 next 方法来启动状态机的迭代器,并逐步输入指定的输入值。在每次迭代中,我们可以通过迭代器的 value 属性获取当前状态,并根据当前状态来决定下一步的操作。

通过以上代码,我们可以使用生成器实现一个简单的状态机,并通过输入不同的指令来控制状态机的运行。使用生成器实现状态机的好处是,可以将状态机的代码结构化和简化,易于维护和修改。

5.javascript迭代器生成器实现职责链,从而实现请求的分发和处理。下面是一个使用迭代器和生成器实现职责链的示例代码:

function* handler1(next) {console.log('Handler 1');yield next();console.log('Handler 1 after');
}

function* handler2(next) {console.log('Handler 2');yield next();console.log('Handler 2 after');
}

function* handler3(next) {console.log('Handler 3');yield next();console.log('Handler 3 after');
}

function* finalHandler() {console.log('Final handler');
}

function runChain() {const chain = [handler1, handler2, handler3, finalHandler];const iterator = chain.reduceRight((next, fn) => () => fn(next), () => {});iterator();
}

runChain(); 

在上面的代码中,我们定义了三个处理器函数 handler1handler2handler3,以及一个最终处理器函数 finalHandler。这些处理器函数接收一个参数 next,表示下一个处理器函数,同时使用 yield 关键字暂停当前函数的执行,并将执行权转移给下一个函数。

我们还定义了一个 runChain 函数,它将所有的处理器函数按顺序存储在一个数组 chain 中,并使用 reduceRight 方法将所有的处理器函数组合成一个迭代器。在迭代器中,我们将下一个函数作为参数传递给当前函数,并将当前函数作为下一个函数的参数传递给前一个函数,从而形成一个职责链。

最后,我们调用 iterator 方法来启动职责链,并从第一个处理器函数开始执行。在每个处理器函数中,我们先输出当前处理器的标识符,然后使用 yield next() 转移执行权给下一个处理器函数。在最后一个处理器函数中,我们不再使用 yield 关键字,而是直接执行最终处理的逻辑。

通过以上代码,我们可以使用迭代器和生成器实现职责链模式,并将请求的分发和处理封装在不同的处理器函数中,从而提高代码的可维护性和扩展性。

总之,在 JavaScript 中,生成器和迭代器是两个非常有用的概念,它们可以帮助我们更加方便地处理数据集合、异步编程等场景。

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

猜你喜欢

转载自blog.csdn.net/web22050702/article/details/129795929