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

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


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


你将学到



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



正文



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.实现一个图片播放器




图片播放器主要有以上几个功能,上一页,下一页,首页,尾页,自动播放按钮,停止按钮。具体组件的设计机构可以参考我写的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设计模式的精髓,决定了我们的高度和态度。

目录
相关文章
|
2月前
|
前端开发 UED 开发者
颠覆你的前端知识:防抖与节流的区别及实战解析!
【8月更文挑战第23天】在Web前端开发中,处理用户界面交互产生的事件可能会影响性能。为此,我们有两种优化方法:防抖(debounce)和节流(throttle)。防抖确保函数仅在事件停止触发一段时间后执行一次,适用于如搜索自动补全场景。而节流则确保函数按固定时间间隔执行,不管用户操作频率如何。本篇技术博客将深入解析两者差异并提供示例代码,帮助开发者更好地理解和应用这些技巧以提升应用性能和用户体验。
63 0
|
3月前
|
前端开发
Element UI 【实战】纯前端对表格数据进行增删改查(内含弹窗表单、数据校验、时间日期格式)
Element UI 【实战】纯前端对表格数据进行增删改查(内含弹窗表单、数据校验、时间日期格式)
123 6
|
29天前
|
缓存 监控 前端开发
前端性能优化实战:让你的网站快如闪电的十大秘籍
【9月更文挑战第3天】通过以上十大秘籍的实践,您可以显著提升网站的前端性能,让您的网站在竞争激烈的互联网环境中脱颖而出,为用户带来更加流畅和愉悦的体验。记住,前端性能优化是一个永无止境的过程,只有不断迭代和优化,才能保持网站的竞争力。
|
2月前
|
编解码 前端开发 开发者
【前端设计达人必备】揭秘CSS尺寸单位的魔力:从基础到实战,打造灵动响应式网页!
【8月更文挑战第26天】本文深入探讨了CSS中常用的尺寸单位,包括像素(px)、百分比(%)、视窗单位(vw/vh/vmin/vmax)、可伸缩相对单位(em/rem)以及Flexbox和Grid中的fr单位。通过具体案例展示了每种单位的特点及其适用场景。像素适用于固定尺寸元素;百分比和em/rem利于构建响应式布局;视窗单位适合全屏设计;fr单位则能有效管理复杂网格布局的空间分配。掌握这些单位有助于开发者设计出更加灵活、高质量的网页布局。
38 4
|
2月前
|
前端开发 JavaScript 开发者
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
本文提供了一个前端JS按钮点击事件、弹出式窗口和遮罩层的实战示例,包括HTML、CSS和JavaScript的具体实现代码,以及功能解析,演示了如何实现按钮点击后触发弹窗显示和遮罩层,并在2秒后自动关闭或点击遮罩层关闭弹窗的效果。
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
|
2月前
|
存储 前端开发 JavaScript
解锁前端高手之路:Vuex 状态管理实战,从零到精通的旅程!
【8月更文挑战第27天】状态管理在大型单页应用开发中至关重要。Vue.js 通过其官方工具 Vuex 提供了一套强大且直观的 API。本文从零开始引导你逐步掌握 Vuex。首先介绍如何安装和配置,然后通过具体示例深入探讨状态(State)、变更(Mutations)、动作(Actions)以及模块化 Store 的使用方法。最后,通过购物车管理实战案例展示如何运用 Vuex 解决复杂状态管理问题。掌握这些技巧后,你将能在项目中高效地利用 Vuex。
18 1
|
2月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
49 0
|
2月前
|
前端开发 Java UED
JSF 面向组件开发究竟藏着何种奥秘?带你探寻可复用 UI 组件设计的神秘之路
【8月更文挑战第31天】在现代软件开发中,高效与可维护性至关重要。JavaServer Faces(JSF)框架通过其面向组件的开发模式,提供了构建复杂用户界面的强大工具,特别适用于设计可复用的 UI 组件。通过合理设计组件的功能与外观,可以显著提高开发效率并降低维护成本。本文以一个具体的 `MessageComponent` 示例展示了如何创建可复用的 JSF 组件,并介绍了如何在 JSF 页面中使用这些组件。结合其他技术如 PrimeFaces 和 Bootstrap,可以进一步丰富组件库,提升用户体验。
44 0
|
2月前
|
API UED 开发者
如何在Uno Platform中轻松实现流畅动画效果——从基础到优化,全方位打造用户友好的动态交互体验!
【8月更文挑战第31天】在开发跨平台应用时,确保用户界面流畅且具吸引力至关重要。Uno Platform 作为多端统一的开发框架,不仅支持跨系统应用开发,还能通过优化实现流畅动画,增强用户体验。本文探讨了Uno Platform中实现流畅动画的多个方面,包括动画基础、性能优化、实践技巧及问题排查,帮助开发者掌握具体优化策略,提升应用质量与用户满意度。通过合理利用故事板、减少布局复杂性、使用硬件加速等技术,结合异步方法与预设缓存技巧,开发者能够创建美观且流畅的动画效果。
57 0
|
2月前
|
前端开发 开发者 Apache
揭秘Apache Wicket项目结构:如何打造Web应用的钢铁长城,告别混乱代码!
【8月更文挑战第31天】Apache Wicket凭借其组件化设计深受Java Web开发者青睐。本文详细解析了Wicket项目结构,帮助你构建可维护的大型Web应用。通过示例展示了如何使用Maven管理依赖,并组织页面、组件及业务逻辑,确保代码清晰易懂。Wicket提供的页面继承、组件重用等功能进一步增强了项目的可维护性和扩展性。掌握这些技巧,能够显著提升开发效率,构建更稳定的Web应用。
76 0