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

徐小夕
• 阅读 1254

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

你将学到

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

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

正文

1.迭代器的含义

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

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

2.实现一个数组迭代器

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

// 数组迭代器
let eachArr = function(arr, fn) {
    let i = 0,
    len = arr.length;
    for(; i < len; i++) {
        if(fn.call(arr[i], i, arr[i]) === false) {
            break;
        }
    }
}

// 使用
eachArr([1,2,3,4], (index, value) => { console.log(index, value) })

3.实现一个对象迭代器

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

// 对象迭代器
let eachObj = function(obj, fn) {
    for(let key in obj) {
        if(fn.call(obj[key], key, obj[key]) === false) {
            break;
        }
    }
}

// 使用
eachObj({a: 11, b: 12}, (key, value) => { console.log(key, value) })

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

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

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

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

let findObjAttr = function(obj, key){
    if(!obj || !key) {
        return undefined
    }
    let result = obj;
    key = key.split('.');
    for(let i =0; len = key.length; i< len; i++) {
        if(result[key[i]] !== undefined) {
            result = result[key[i]]
        }else {
            return undefined
        }
    }
    return result
}
// 使用
let a = { b: { c: { d: 1 } } };
findObjAttr(a, 'a.b.c.d')     // 1

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

let setObjAttr = function(obj, key, value){
    if(!obj) {
        return false
    }
    let result = obj,
    key = key.split('.');
    for(let i =0, len = key.length; i< len - 1; i++){
        if(result[key[i]] === undefined) {
            result[key[i]] = {};
        }

        if(!(result[key[i]] instanceof Object)){
            // 如果第i层对应的不是一个对象,则剖出错误
            throw new Error('is not Object')
            return false
        }

        result = result[key[i]]
    }
    return result[key[i]] = val
}

// 使用
setObjAttr(obj, 'a.b.c.d', 'xuxi')

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

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

 // 数据分组
 function group(name, num) {
     let data = [];
     for(let i = 0; i < num; i++){
         switch(name) {
             case 'header':
                data[i][0] = 0;
                data[i][1] = 1;
                break;
            case 'content':
                data[i][0] = 2;
                data[i][1] = 3;
                break;
            case 'footer':
                data[i][0] = 4;
                data[i][1] = 532;
                break;
            default:
                break;
         }
     }
     return data
 }

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

 // 数据分组
 function group(name, num) {
     let data = [];
     let strategy = function() {
         let deal = {
             'default': function(i){
                 return
             },
             'header': function(i){
                data[i][0] = 0;
                data[i][1] = 1;
             },
            'content': function(i){
                data[i][0] = 2;
                data[i][1] = 3;
             }, 
             //...
         }
         return function(name) {
             return deal[name] || deal['default']
         }
     }();
     // 迭代器处理数据
     function _each(fn) {
        for(let i = 0; i < num; i++){
         fn(i)
        }
     }

     _each(strategy(name))

     return data
 }

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

6.实现一个图片播放器

《前端实战总结》之迭代器模式的N+1种应用场景 图片播放器主要有以上几个功能,上一页,下一页,首页,尾页,自动播放按钮,停止按钮。具体组件的设计机构可以参考我写的demo:

// 图片播放器
let imgPlayer = function(imgData, box) {
    let container = box && document.querySelector(box) || document,
    img = container.querySelector('img'),
    // 获取图片长度
    len = imgData.length,
    // 当前索引值
    index = 0;
    // 初始化图片
    img.src = imgData[0];

    var timer = null;

    return {
        // 获取第一个图片
        first: function() {
            index = 0
            img.src = imgData[index]
        },
        // 获取最后一个图片
        last: function() {
            index = len - 1
            img.src = imgData[index]
        },
        // 切换到前一张图片
        pre: function() {
            if(--index > 0) {
                img.src = imgData[index]
            }else {
                index = 0
                img.src = imgData[index]
            }
        },
        // 切换到后一张图片
        next: function() {
            if(++index < len) {
                img.src = imgData[index]
            }else {
                index = len - 1
                img.src = imgData[index]
            }
        },
        // 自动播放图片
        play: function() {
            timer = setInterval(() => {
                if(index > len - 1) {
                    index = 0
                }
                img.src = imgData[index]
                index++
            }, 5000)
        },
        // 停止播放图片
        stop: function() {
            clearInterval(timer)
        }
    }
}

// 使用
let player = new imgPlayer(imgData, '#box')

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

最后

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

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

更多推荐

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
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
徐小夕 徐小夕
3年前
《前端实战总结》之使用解释器模式实现获取元素Xpath路径的算法
前端领域里基于javascript的设计模式和算法有很多,在很多复杂应用中也扮演着很重要的角色,接下来就介绍一下javascript设计模式中的解释器模式,并用它来实现一个获取元素Xpath路径的算法。上期回顾《前端实战总结》之迭代器模式的N1种应用场景(https://juejin.im/post/6844904008616771591)
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这