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

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

你将学到

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

正文

1.迭代器的含义

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

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

2.实现一个数组迭代器

咱们都知道javascript中数组的forEach方法,那么不用这个方法,咱们能本身实现一个吗?vue

// 数组迭代器
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.实现一个对象迭代器

对象迭代器和数组迭代器相似, 只是传参不一样,以下:java

// 对象迭代器
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.实现路径查找/赋值迭代器

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

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

咱们经过迭代器能够极大的减小这种校验,实现更健壮的代码模式:webpack

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的对象/数组查找器呢?同理,咱们也能够实现路径赋值器,以下所示:css3

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.如何用迭代器的思想解决分支循环嵌套问题

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

// 数据分组
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设计模式的精髓,决定了咱们的高度和态度。

最后

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

更多推荐

相关文章
相关标签/搜索