《前端實戰總結》之迭代器模式的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等前端知識和實戰,歡迎在公衆號《趣談前端》加入咱們一塊兒學習討論,共同探索前端的邊界。

更多推薦

相關文章
相關標籤/搜索