一、生成器与迭代器的应用
-
遍历
遍历的核心就是从一个容器里面东西都拿出来
每一次遍历的过程中,要对遍历的结果进行处理,而不是每一次都要处理的情况,那么用遍历本身是不合适的
-
迭代与遍历区别
-
遍历
把容器里的所有东西都观察一遍、拿一遍
-
迭代
在本次遍历的过程中进行一次程序上的输出
在遍历的过程当中,循环到当前这一次的时候,就是整个循环功能的一次迭代
项目迭代:根据本次的结果进行一次更新,进行一次修复,进行一次小的功能叠加
在遍历过程当中进行某一次程序的输出的话,那么就必须要有一个叫迭代器的东西
-
-
迭代器(iterator)
迭代器可以在遍历的时候一次一次的执行,而不是整个都执行,它会根据你的指令去遍历指定次数
但它有一个问题:就是迭代器还不能直接使用,因为迭代器是建立在遍历的基础上的,不可能遍历某一次,需要借助生成器
-
生成器(generator)
-
概念
就是生产迭代器的东西
由于代器是建立在遍历的基础上的,而生成器就是利用遍历造出一个迭代器
-
生成器及yield
这就是一个生成器,实例化出来的就是一个迭代器
function * test() {}
但它还是需要依赖遍历,它生产迭代器的关键点就在于yield,因为yield可以让这个遍历停下来
var arr = [1, 2, 3, 4, 5, 6]; function * test(arr) { for (var item of arr) { yield item; } } let iterator = test(arr); // 实例化(生产)出来一个迭代器,传入迭代对象
-
next
对迭代器进行迭代,返回值是
{ value: xxx, done: boolean }
(value是抽取的值(对应yield产出的值),done则是否迭代完毕)var arr = [1, 2, 3, 4, 5, 6]; function* test(arr) { for (var item of arr) { yield item; } } let iterator = test(arr); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: 4, done: false } console.log(iterator.next()); // { value: 5, done: false } console.log(iterator.next()); // { value: 6, done: false } console.log(iterator.next()); // { value: undefined, done: true }
-
-
ES5实现生成器
var arr = [1, 2, 3, 4, 5, 6]; function generator(arr) { var i = 0; return { next() { var done = i > arr.length ? true : false, value = done ? 'undefined' : arr[i++]; return { value: value, done: done } } } } let iterator = generator(arr); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: 4, done: false } console.log(iterator.next()); // { value: 5, done: false } console.log(iterator.next()); // { value: 6, done: false } console.log(iterator.next()); // { value: undefined, done: true }
-
需求
-
从test1依次执行到test5
var functions = [ function test1() { console.log('test1'); }, function test2() { console.log('test2'); }, function test3() { console.log('test3'); }, function test4() { console.log('test4'); }, function test5() { console.log('test5'); } ]; for (let item of functions) { item(); } // test1 // test2 // test3 // test4 // test5
-
执行到某一个地方截断(执行到test3停止 )
var functions = [ function test1() { console.log('test1'); return true; }, function test2() { console.log('test2'); return true; }, function test3() { console.log('test3'); return false; }, function test4() { console.log('test4'); return true; }, function test5() { console.log('test5'); return true; } ]; for (let item of functions) { if (!item()) { break; }; } // test1 // test2 // test3
这种截断是不可取的,因为它是建立在遍历的基础上的,而我们必须建立在迭代的基础上
-
-
中间件(node express)
中间件集合:[test1,test2,test3,test4](token是否存在 => token是否合法 => token是否过期 => 打开页面)
当用户访问一个路由
/user
的时候,这些中间件集合就会执行但只要其中一个中间件有问题,就会截断后续中间件的执行
在学习express、koa的时候,你会发现每一个中间件函数里面都会存在一个next的东西,也就是说在next执行的时候才会去执行下一个中间件函数
var functions = [ function test1(next) { console.log('test1'); next(); // 如果中间有哪一个next没有执行,那么后续的中间件是不会执行的,这就是迭代过程中的截断 }, function test2(next) { console.log('test2'); next(); }, function test3(next) { console.log('test3'); next(); }, function test4(next) { console.log('test4'); next(); }, function test5(next) { console.log('test5'); next(); } ];
-
生成器实现中间件
-
源码实现
;(function(functions) { function* generator(arr) { for (var i = 0; i < arr.length; i++) { yield arr[i]; } } var iterator = generator(functions); var init = () => { nextDo(iterator.next()); } function nextDo(n) { n.value(function() { // 这个函数其实就是next,为什么要传这个函数呢,因为它是连续执行的关键,如果next有执行就继续往下执行,如果没有就截断,而继续往下执行就是通过nextDo递归的方式实现的,递归时会判断是否迭代完成,如果否则继续迭代,如果是就终止递归 var n = iterator.next(); // 拿到当前的迭代器对象 if (!n.done) { // 判断时候迭代完成,如果没有就继续迭代,如果是就终止递归 nextDo(n); } else { return } }); } init(); })( [ function test1(next) { console.log('test1'); next(); }, function test2(next) { console.log('test2'); next(); }, function test3(next) { console.log('test3'); // 判断是否有token,有就往下执行,没有就什么都不做 //if(token){ // next(); //} next(); }, function test4(next) { console.log('test4'); next(); }, function test5(next) { console.log('test5'); next(); } ] ); // test1 // test2 // test3 // test4 // test5
-
插件使用
// 导入中间件插件 import M from "./middleware.js"; M([checkInputValue, submitData, loginSuccess]) // 表单验证 function checkInputValue(){ if(......) { next(); } } // 登录提交 function submitData() { $.ajax({ ...... success: function(data){ if(xxxx) { next(); } } }) } // 登录成功 function loginSuccess() { location.href = '......' }
-
-
迭代器使用场景 —— 操作日志
index.html
<input type="text" id="content"> <button id="btn">操作</button> <ul class="log-list"></ul> <script src="./index.js"></script>
index.js
;(() => { var oContent = document.getElementById('content'), oBtn = document.getElementById('btn'), oList = document.getElementsByClassName('log-list')[0]; let log = [], it = generator(log); const init = () => { bindEvent(); } function bindEvent() { oBtn.addEventListener('click', handleBtnClick, false); } function handleBtnClick() { const value = oContent.value; log.push({ value, dateTime: new Date() }) _addLog(it.next().value); } function _addLog(log) { const oLi = document.createElement('li'); oLi.innerHTML = ` <p>增加一项:${log.value}</p> <p>操作事件:${log.dateTime}</p> ` oList.appendChild(oLi); } function* generator(arr) { for (let item of arr) { yield item; } } init(); })();
迭代器的迭代是有记忆的,它会基于上一次进行下一次迭代,就不用考虑用下标,这也是它好用的地方
生成器、迭代器用得更多的还是底层封装,比如TJ的co库就用了很多(异步转同步的库),以后学了node可以多看看