《前端实战总结》之迭代器模式的N+1种应用场景

眼看12月就来了,抓住今年的尾巴,好好总结一下前端的不足与收获。这篇文章是笔者写设计模式专题的第二篇文章,也是基于工作中的总结和提炼,在实际应用场景中都会大量使用,至于为什么要写设计模式,主要是为了提高团队代码质量和可维护性,后续会继续推出设计模式相关的文章,供大家参考和学习。

你将学到

  • 迭代器模式的含义
  • 实现一个数组迭代器
  • 实现一个对象迭代器
  • 实现路径查找/赋值迭代器
  • 如何用迭代器的思想解决分支循环嵌套问题
  • 实现一个图片播放器

正文

1.迭代器的含义

迭代器模式主要的思想就是在不暴露对象内部结构的同时可以按照一定顺序访问对象内部的元素。

其实javascript中的很多方法都运用了迭代器的思想,比如数组的forEach,every,find,some,map,entries等等,这些操作极大的简化了我们的逻辑操作,接下来我们就来看看它的具体应用吧。

2.实现一个数组迭代器

我们都知道javascript中数组的forEach方法,那么不用这个方法,我们能自己实现一个吗?

1// 数组迭代器 2let eachArr = function(arr, fn) { 3 let i = 0, 4 len = arr.length; 5 for(; i < len; i++) { 6 if(fn.call(arr[i], i, arr[i]) === false) { 7 break; 8 } 9 } 10} 11 12// 使用 13eachArr([1,2,3,4], (index, value) => { console.log(index, value) })

3.实现一个对象迭代器

对象迭代器和数组迭代器类似, 只是传参不同,如下:

1// 对象迭代器 2let eachObj = function(obj, fn) { 3 for(let key in obj) { 4 if(fn.call(obj[key], key, obj[key]) === false) { 5 break; 6 } 7 } 8} 9 10// 使用 11eachObj({a: 11, b: 12}, (key, value) => { console.log(key, value) })

4.实现路径查找/赋值迭代器

有时候我们操作对象的某些属性时,我们不知道服务器端是否将该属性或者该属性的上级属性正确的返回给我们,这个时候我们直接通过点语法或者[]语法直接访问会导致代码报错,因此需要我们每一层操作都要做安全校验,这样会产生大量臃肿代码,比如:

1let obj = {}; 2// 获取 obj.num.titNum 3let titNum = obj.num.titNum; // 报错 4let titNum = obj && obj.num && obj.num.titNum; // 正确

我们通过迭代器可以极大的减少这种校验,实现更健壮的代码模式:

1let findObjAttr = function(obj, key){ 2 if(!obj || !key) { 3 return undefined 4 } 5 let result = obj; 6 key = key.split('.'); 7 for(let i =0; len = key.length; i< len; i++) { 8 if(result[key[i]] !== undefined) { 9 result = result[key[i]] 10 }else { 11 return undefined 12 } 13 } 14 return result 15} 16// 使用 17let a = { b: { c: { d: 1 } } }; 18findObjAttr(a, 'a.b.c.d') // 1

这种方式是不是有点类似于lodash的对象/数组查找器呢?同理,我们也可以实现路径赋值器,如下所示:

1let setObjAttr = function(obj, key, value){ 2 if(!obj) { 3 return false 4 } 5 let result = obj, 6 key = key.split('.'); 7 for(let i =0, len = key.length; i< len - 1; i++){ 8 if(result[key[i]] === undefined) { 9 result[key[i]] = {}; 10 } 11 12 if(!(result[key[i]] instanceof Object)){ 13 // 如果第i层对应的不是一个对象,则剖出错误 14 throw new Error('is not Object') 15 return false 16 } 17 18 result = result[key[i]] 19 } 20 return result[key[i]] = val 21} 22 23// 使用 24setObjAttr(obj, 'a.b.c.d', 'xuxi')

5.如何用迭代器的思想解决分支循环嵌套问题

分支循环嵌套的问题主要是指在循环体中还需要进行额外的判断,如果判断条件变多,将会造成严重的性能开销问题,如下面的例子:

1// 数据分组 2function group(name, num) { 3 let data = []; 4 for(let i = 0; i < num; i++){ 5 switch(name) { 6 case 'header': 7 data[i][0] = 0; 8 data[i][1] = 1; 9 break; 10 case 'content': 11 data[i][0] = 2; 12 data[i][1] = 3; 13 break; 14 case 'footer': 15 data[i][0] = 4; 16 data[i][1] = 532; 17 break; 18 default: 19 break; 20 } 21 } 22 return data 23}

由以上分析可知,上面的代码还有很多优化空间,因为每一次遍历都要进行一次分支判断,那么如果num变成100000,且name的种类有100种,那么我们就要做100000*100种无用的分支判断,这样无疑会让你的代码在大数据下卡死。不过我们可以通过以下这种方式优化它:

1// 数据分组 2function group(name, num) { 3 let data = []; 4 let strategy = function() { 5 let deal = { 6 'default': function(i){ 7 return 8 }, 9 'header': function(i){ 10 data[i][0] = 0; 11 data[i][1] = 1; 12 }, 13 'content': function(i){ 14 data[i][0] = 2; 15 data[i][1] = 3; 16 }, 17 //... 18 } 19 return function(name) { 20 return deal[name] || deal['default'] 21 } 22 }(); 23 // 迭代器处理数据 24 function _each(fn) { 25 for(let i = 0; i < num; i++){ 26 fn(i) 27 } 28 } 29 30 _each(strategy(name)) 31 32 return data 33}

这样我们就能避免分支判断,极大的提高了代码效率和性能。

6.实现一个图片播放器

图片播放器主要有以上几个功能,上一页,下一页,首页,尾页,自动播放按钮,停止按钮。具体组件的设计机构可以参考我写的demo:

1// 图片播放器 2let imgPlayer = function(imgData, box) { 3 let container = box && document.querySelector(box) || document, 4 img = container.querySelector('img'), 5 // 获取图片长度 6 len = imgData.length, 7 // 当前索引值 8 index = 0; 9 // 初始化图片 10 img.src = imgData[0]; 11 12 var timer = null; 13 14 return { 15 // 获取第一个图片 16 first: function() { 17 index = 0 18 img.src = imgData[index] 19 }, 20 // 获取最后一个图片 21 last: function() { 22 index = len - 1 23 img.src = imgData[index] 24 }, 25 // 切换到前一张图片 26 pre: function() { 27 if(--index > 0) { 28 img.src = imgData[index] 29 }else { 30 index = 0 31 img.src = imgData[index] 32 } 33 }, 34 // 切换到后一张图片 35 next: function() { 36 if(++index < len) { 37 img.src = imgData[index] 38 }else { 39 index = len - 1 40 img.src = imgData[index] 41 } 42 }, 43 // 自动播放图片 44 play: function() { 45 timer = setInterval(() => { 46 if(index > len - 1) { 47 index = 0 48 } 49 img.src = imgData[index] 50 index++ 51 }, 5000) 52 }, 53 // 停止播放图片 54 stop: function() { 55 clearInterval(timer) 56 } 57 } 58} 59 60// 使用 61let player = new imgPlayer(imgData, '#box')

总之,迭代器思想和其他设计模式的组合,可以设计出各种各样高度配置的组件,所以说学好并理解javascript设计模式的精髓,决定了我们的高度和态度。

最后

如果想了解更多webpack,node,gulp,css3,javascript,nodeJS,canvas等前端知识和实战,欢迎在公众号《趣谈前端》加入我们一起学习讨论,共同探索前端的边界。

更多推荐

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

一篇文章带你了解JavaScript日期

日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用