Underscore源碼解析(二)

本文同步自我得博客:http://www.joeray61.comnode

最近十幾天都在忙畢業論文的事,因此上一次爲你們介紹完underscore這個框架的結構(或者說是這個框架的設計思路)以後就一直沒動靜了,今天我又滿血復活了,讓咱們繼續來探索underscore的源碼奧祕吧。
沒看過上一篇文章的朋友能夠戳這裏:underscore源碼解析(一)
今天的內容是underscore裏面封裝的一些函數,我將逐個介紹,我們直接入正題吧segmentfault

each / _.each / _.forEach

var each = _.each = _.forEach = function(obj, iterator, context) {
    // 不處理空值
    if(obj == null)
        return;
    if(nativeForEach && obj.forEach === nativeForEach) {
        // 若是宿主環境支持, 則優先調用JavaScript 1.6提供的forEach方法
        obj.forEach(iterator, context);
    } else if(obj.length === +obj.length) {
        // 對[數組]中每個元素執行處理器方法
        for(var i = 0, l = obj.length; i < l; i++) {
            if( i in obj && iterator.call(context, obj[i], i, obj) === breaker)
                return;
        }
    } else {
        // 對{對象}中每個元素執行處理器方法
        for(var key in obj) {
            if(_.has(obj, key)) {
                if(iterator.call(context, obj[key], key, obj) === breaker)
                    return;
            }
        }
    }
};

這個函數的實現思想其實很簡單,若是宿主環境(通常爲瀏覽器或者node.js)支持原生的forEach方法,就調用原生的,不然就遍歷該數組或者對象,依次調用處理器方法
值得一提的是在判斷是不是數組的時候,這裏的代碼爲數組

obj.length === +obj.length瀏覽器

這實際上是一種鴨式辨型的斷定方法,具體能夠參見我在SF上提過的一個問題:點我框架

_.map / _.collect

_.map = _.collect = function(obj, iterator, context) {
    // 用於存放返回值的數組
    var results = [];
    if(obj == null)
        return results;
    // 優先調用宿主環境提供的map方法
    if(nativeMap && obj.map === nativeMap)
        return obj.map(iterator, context);
    // 迭代處理集合中的元素
    each(obj, function(value, index, list) {
        // 將每次迭代處理的返回值存儲到results數組
        results[results.length] = iterator.call(context, value, index, list);
    });
    // 返回處理結果
    if(obj.length === +obj.length)
        results.length = obj.length;
    return results;
};

map/collect函數與each的區別在於map/collect會存儲每次迭代的返回值, 並做爲一個新的數組返回ide

_.reduce / _.foldl / _.inject

_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
    // 經過參數數量檢查是否存在初始值
    var initial = arguments.length > 2;
    if(obj == null)
        obj = [];
    // 優先調用宿主環境提供的reduce方法
    if(nativeReduce && obj.reduce === nativeReduce && false) {
        if(context)
            iterator = _.bind(iterator, context);
        return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    }
    // 迭代處理集合中的元素
    each(obj, function(value, index, list) {
        if(!initial) {
            // 若是沒有初始值, 則將第一個元素做爲初始值; 若是被處理的是對象集合, 則默認值爲第一個屬性的值
            memo = value;
            initial = true;
        } else {
            // 記錄處理結果, 並將結果傳遞給下一次迭代
            memo = iterator.call(context, memo, value, index, list);
        }
    });
    if(!initial)
        throw new TypeError('Reduce of empty array with no initial value');
    return memo;
};

這個函數的做用是將集合中每一個元素放入迭代處理器, 並將本次迭代的返回值做爲memo傳遞到下一次迭代, 通常用於累計結果或鏈接數據函數

_.reduceRight / _.foldr

_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
    var initial = arguments.length > 2;
    if(obj == null)
        obj = [];
    // 優先調用宿主環境提供的reduceRight方法
    if(nativeReduceRight && obj.reduceRight === nativeReduceRight) {
        if(context)
            iterator = _.bind(iterator, context);
        return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
    }
    // 逆轉集合中的元素順序
    var reversed = _.toArray(obj).reverse();
    if(context && !initial)
        iterator = _.bind(iterator, context);
    // 經過reduce方法處理數據
    return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
};

這個函數與reduce類似,不過它是逆向迭代集合中的元素prototype

_.find / _.detect

_.find = _.detect = function(obj, iterator, context) {
    // result存放第一個可以經過驗證的元素
    var result;
    // 經過any方法遍歷數據, 並記錄經過驗證的元素
    any(obj, function(value, index, list) {
        // 若是處理器返回的結果被轉換爲Boolean類型後值爲true, 則記錄當前值並返回當前元素
        if(iterator.call(context, value, index, list)) {
            result = value;
            return true;
        }
    });
    return result;
};

這個方法的做用是遍歷集合中的元素, 返回可以經過處理器驗證的第一個元素設計

_.filter / _.select

_.filter = _.select = function(obj, iterator, context) {
    // 用於存儲經過驗證的元素數組
    var results = [];
    if(obj == null)
        return results;
    // 優先調用宿主環境提供的filter方法
    if(nativeFilter && obj.filter === nativeFilter)
        return obj.filter(iterator, context);
    // 迭代集合中的元素, 並將經過處理器驗證的元素放到數組中並返回
    each(obj, function(value, index, list) {
        if(iterator.call(context, value, index, list))
            results[results.length] = value;
    });
    return results;
};

這個方法與find做用相似, 但它會記錄下集合中全部經過驗證的元素3d

_.reject

_.reject = function(obj, iterator, context) {
    var results = [];
    if(obj == null)
        return results;
    each(obj, function(value, index, list) {
        if(!iterator.call(context, value, index, list))
            results[results.length] = value;
    });
    return results;
};

這個方法的代碼裏面我沒有加註釋,由於整個代碼與filter/select方法幾乎同樣,不一樣點在於向results數組裏添加元素的時候判斷條件是相反的,也就是說這個方法的做用是返回沒有經過處理器驗證的元素列表

_.every / _.all

_.every = _.all = function(obj, iterator, context) {
    var result = true;
    if(obj == null)
        return result;
    // 優先調用宿主環境提供的every方法
    if(nativeEvery && obj.every === nativeEvery)
        return obj.every(iterator, context);
    // 迭代集合中的元素
    each(obj, function(value, index, list) {
        // 這裏我不太理解,爲何藥寫成 result = (result && iterator.call(context, value, index, list)) 而不是 result = iterator.call(context, value, index, list)
        if(!( result = result && iterator.call(context, value, index, list)))
            return breaker;
    });
    return !!result;
};

這個方法的做用是若是集合中全部元素均能經過處理器驗證, 則返回true

any / _.some / _.any

var any = _.some = _.any = function(obj, iterator, context) {
    // 若是沒有指定處理器參數, 則使用默認的處理器函數,該函數會返回參數自己
    iterator || ( iterator = _.identity);
    var result = false;
    if(obj == null)
        return result;
    // 優先調用宿主環境提供的some方法
    if(nativeSome && obj.some === nativeSome)
        return obj.some(iterator, context);
    // 迭代集合中的元素
    each(obj, function(value, index, list) {
        if(result || ( result = iterator.call(context, value, index, list)))
            return breaker;
    });
    return !!result;
};

該函數的做用是檢查集合中是否有任何一個元素在被轉換成Boolean類型時是否爲true

_.include / _.contains

_.include = _.contains = function(obj, target) {
    var found = false;
    if(obj == null)
        return found;
    // 優先調用宿主環境提供的Array.prototype.indexOf方法
    if(nativeIndexOf && obj.indexOf === nativeIndexOf)
        return obj.indexOf(target) != -1;
    // 經過any方法迭代集合中的元素, 驗證元素的值和類型與目標是否徹底匹配
    found = any(obj, function(value) {
        return value === target;
    });
    return found;
};

這個函數用於檢查集合中是否有值與目標參數徹底匹配,包括數據類型

小結

今天先介紹以上10個函數的實現細節,以後還會繼續帶來其餘函數的介紹,歡迎你們提出指正和建議,thx for reading, hope u enjoy

相關文章
相關標籤/搜索