打造屬於自己的underscore系列(四)- 迭代器(下)

不做祖國的韭菜發表於2019-01-29

接上一節迭代器的話題,在打造underscore系列的第三篇中,我們引入了迭代器的感念,並實現了reduce, map, times等常用的迭代器方法,由於篇幅過長,我們將其他迭代器的實現放在了這一小節中。

3.5 _.each - _.each(list, iteratee, [context])

_.each 和 _.foreach 方法本質上是定義的同一個方法,在打造屬於自己的underscore系列 ( 一 )的框架原理中,我們對 _.each的方法做了實現,其中call改變this指向的程式碼我們是這樣實現的。

if (_.isArray(target)) {
    var length = target.length;
    for (; i < length; i++) {
        callback.call(target, target[i], i);
    }
} else {
    for (key in target) {
        callback.call(target, key, target[key]);
    }
}
複製程式碼

而在系列三小節內容中,我們優化了this指向這部分程式碼,具體封裝為optimizeCb 函式,詳見打造屬於自己的underscore系列 ( 三 ),這裡不做重複贅述。經過迭代器優化後,each的實現如下, 其中迭代器引數型別為3,即 func.call(context, value, index)

// 遍歷 陣列  物件
    _.each = _.forEach =  function (target, callback, context) {
        iteratee = optimizeCb(callback, context, 3) // 迭代器優化
        var key, i = 0;
        if (_.isArray(target)) {
            var length = target.length;
            for (; i < length; i++) {
                iteratee(target[i], i);
            }
        } else {
            for (key in target) {
                iteratee(key, itarget[key])
            }
        }
    }
複製程式碼
3.6 _.some() - _.some(list, [predicate], [context])

與原生js中some的方法相同,some的迭代器會遍歷元素,同時進行真值檢測,一旦有元素通過真值檢測,則返回true。因此,我們首先需要了解什麼是真值檢測? 簡單的理解,真值檢測就是條件判斷,即將檢測的值轉換Boolean型別,判斷轉換後的值是否為true。而在條件判斷中,我們做了一個總結,除了 undefined, null, false, NaN, '', 0, -0,其他值都會轉換為true, 包括空物件。eg

if(null) {console.log('null')}
if(0) {console.log(2)}
if('222'){ console.log('string')}
if(false) { console.log(false)}
if(NaN) { console.log('nan')}
if(undefined) { console.log('undefined')}
if('') { console.log('空')}
if(-0) { console.log('-0')}
if({}) { console.log('object')}

// 'string', 'object'
複製程式碼

同樣遍歷的list可以為物件或者陣列,因此類似的_some的實現可以類比之前的例子。

_.some = _.any = function(list, iteratee, context) {
    var keys = !isArrayLike(list) && _.keys(list);
    var lengths = (keys || list).length;
    for(var index = 0; index < length; index++ ) {
        var currentKey = keys? keys[index] : index;
        if(currentKey) return true
    }
    return false
}
複製程式碼

some方法和map方法在使用方式上的相同點在於,函式的第二個引數iteratee不僅僅侷限於函式,還可以不傳值,或者傳遞物件等,因此同樣的處理方式我們可以用前一節總結的回撥函式進行優化。

// 系列的第三節總結的cb方法
var cb = function (iteratee, context, args) {
    if (iteratee == null) return _.identity;
    if (_.isFunction(iteratee)) return optimizeCb(iteratee, context, args);  //optimizeCb優化迭代器
    if (_.isObject(iteratee) && !_.isArray(value)) return _.matcher(value);
    //其他
}
_.some = _.any = function(list, iteratee, context) {
    predicate = cb(iteratee, context, 3)
    var keys = !isArrayLike(list) && _.keys(list);
    var lengths = (keys || list).length;
    for(var index = 0; index < lengths; index++ ) {
        var currentKey = keys? keys[index] : index;
        if(predicate(list[currentKey], currentKey, list)) return true // 只要某一項的真值檢測通過,則停止遍歷,返回true,所有都不通過時,結果才為false
    }
    return false
}
複製程式碼
3.7 _.every() - _.every(list, [predicate], [context])

和some方法相反,every方法會遍歷list並進行真值檢測,只有當所有檢測值返回true時,結果才會返回true。有了some實現為基礎,every方法的實現顯得格外簡單

_.every = _.all = function(list, iteratee, context) {
    predicate = cb(iteratee, context, 3)
    var keys = !isArrayLike(list) && _.keys(list);
    var lengths = (keys || list).length;
    for(var index = 0; index < lengths; index++ ) {
        var currentKey = keys? keys[index] : index;
        if(!predicate(list[currentKey], currentKey, list)) return false;// 當某一項真值檢測不通過時,則停止遍歷,返回false,只有所有值都通過時,結果才為true
    }
    return true
}
複製程式碼
3.8 _.filter() - _.filter(list, predicate, [context])

filter方法同樣會對list進行遍歷,並對每個遍歷元素進行真值判斷,和every,some不同的是,filter會將所有通過真值檢測的元素組成一個新的陣列返回。因此,只要簡單的處理真值判斷後的語句即可完成filter的功能

_.filter = _.select = function(list, iteratee, context) {
    predicate = cb(iteratee, context, 3)
    var keys = !isArrayLike(list) && _.keys(list);
    var lengths = (keys || list).length;
    var results = [];
    for(var index = 0; index < lengths; index++ ) {
        var currentKey = keys? keys[index] : index;
        if(predicate(list[currentKey], currentKey, list)) results.push(list[currentKey])
    }
    return results
}
複製程式碼

至此,underscore中關於迭代器相關方法的應用場景和實現思路基本介紹完畢。我們知道,迭代器在日常程式碼邏輯編寫中必不可少,且使用頻率較高。熟練掌握迭代器的應用場景並深入理解其中實現思路有利於現實複雜的業務功能的實現。




相關文章