Underscore源码剖析(三)

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

前两天在微博上看到SF的微博引荐了我的前两篇文章,有点不测和欣喜。作为一个菜鸟,真的是倍受鼓舞,我写博客的动力也更足够了。
没看过前两篇博客的朋侪能够戳这里:Underscore源码剖析(一)Underscore源码剖析(二)
上一篇文章引见了underscore的10个函数的详细完成细节,本日将继承引见其他的函数。

_.invoke

_.invoke = function(obj, method) {
    // 挪用同名要领时通报的参数(从第3个参数最先)
    var args = slice.call(arguments, 2);
    // 顺次挪用每一个元素的要领, 并将效果放入数组中返回
    return _.map(obj, function(value) {
        return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
    });
};

这个函数顺次挪用鸠合中一切元素的同名要领,从第3个参数最先的一切参数将被传入到元素的挪用要领中,末了返回一个数组,该数组存储了一切要领的处置惩罚效果

_.pluck

_.pluck = function(obj, key) {
    // 假如某一个对象中不存在该属性, 则返回undefined
    return _.map(obj, function(value) {
        return value[key];
    });
};

这个函数遍历了一个由对象列表构成的鸠合,并返回每一个对象中的指定属性的值列表

_.max

_.max = function(obj, iterator, context) {
    // 假如鸠合是一个数组, 且没有运用处置惩罚器, 则运用Math.max猎取最大值
    // 平常会是在一个数组存储了一系列Number范例的数据
    if(!iterator && _.isArray(obj) && obj[0] === +obj[0])
        return Math.max.apply(Math, obj);
    // 关于空值, 直接返回负无穷大
    if(!iterator && _.isEmpty(obj))
        return -Infinity;
    // 一个暂时的对象, computed用于在比较历程当中存储最大值(暂时的)
    var result = {
        computed : -Infinity
    };
    // 迭代鸠合中的元素
    each(obj, function(value, index, list) {
        // 假如指定了处置惩罚器参数, 则比较的数据为处置惩罚器返回的值, 不然直接运用each遍用时的默许值
        var computed = iterator ? iterator.call(context, value, index, list) : value;
        // 假如比较值比拟上一个值要大, 则将当前值放入result.value
        computed >= result.computed && ( result = {
            value : value,
            computed : computed
        });
    });
    // 返回最大值
    return result.value;
};

望文生义,这个函数用来返回鸠合中的最大值, 假如不存在可比较的值, 则返回undefined

_.min

_.min = function(obj, iterator, context) {
    if(!iterator && _.isArray(obj) && obj[0] === +obj[0])
        return Math.min.apply(Math, obj);
    if(!iterator && _.isEmpty(obj))
        return Infinity;
    var result = {
        computed : Infinity
    };
    each(obj, function(value, index, list) {
        var computed = iterator ? iterator.call(context, value, index, list) : value;
        computed < result.computed && ( result = {
            value : value,
            computed : computed
        });
    });
    return result.value;
};

这个函数没有加解释,由于完成历程与max基础雷同,用于返回鸠合中的最小值

_.shuffle

_.shuffle = function(obj) {
    // shuffled变量存储处置惩罚历程及终究的效果数据
    var shuffled = [], rand;
    // 迭代鸠合中的元素
    each(obj, function(value, index, list) {
        // 天生一个随机数, 随机数在<0-当前已处置惩罚的数目>之间
        rand = Math.floor(Math.random() * (index + 1));
        // 将已随机获得的元素放到shuffled数组末端
        shuffled[index] = shuffled[rand];
        // 在前面获得的随机数的位置插进去最新值
        shuffled[rand] = value;
    });
    // 返回一个数组, 该数组中存储了经由随机混排的鸠合元素
    return shuffled;
};

这个函数是经由过程随机数, 让数组不必分列,实际上是完成了一个模仿洗牌历程的算法

_.sortBy

_.sortBy = function(obj, val, context) {
    // val应当是对象的一个属性, 或一个处置惩罚器函数, 假如是一个处置惩罚器, 则应当返回须要举行比较的数据
    var iterator = _.isFunction(val) ? val : function(obj) {
        return obj[val];
    };
    // 挪用递次: _.pluck(_.map().sort());
    // 挪用_.map()要领遍历鸠合, 并将鸠合中的元素放到value节点, 将元素中须要举行比较的数据放到criteria属性中
    // 挪用sort()要领将鸠合中的元素依据criteria属性中的数据举行递次排序
    // 挪用pluck猎取排序后的对象鸠兼并返回
    return _.pluck(_.map(obj, function(value, index, list) {
        return {
            value : value,
            criteria : iterator.call(context, value, index, list)
        };
    }).sort(function(left, right) {
        var a = left.criteria, b = right.criteria;
        if(a ===
            void 0)
            return 1;
        if(b ===
            void 0)
            return -1;
        return a < b ? -1 : a > b ? 1 : 0;
    }), 'value');
};

这个函数对鸠合中元素, 依据特定的字段或值举行分列,比拟Array.prototype.sort要领, sortBy要领支撑对对象排序

_.groupBy

_.groupBy = function(obj, val) {
    var result = {};
    // val将被转换为举行分组的处置惩罚器函数, 假如val不是一个Function范例的数据, 则将被作为挑选元素时的key值
    var iterator = _.isFunction(val) ? val : function(obj) {
        return obj[val];
    };
    // 迭代鸠合中的元素
    each(obj, function(value, index) {
        // 将处置惩罚器的返回值作为key, 并将雷同的key元素放到一个新的数组
        var key = iterator(value, index);
        (result[key] || (result[key] = [])).push(value);
    });
    // 返回已分组的数据
    return result;
};

这个函数将鸠合中的元素, 按处置惩罚器返回的key分为多个数组

_.sortedIndex

_.sortedIndex = function(array, obj, iterator) {
    // 假如没有指定处置惩罚器参数, 则运用默许的处置惩罚器函数,该函数会返回参数自身
    iterator || ( iterator = _.identity);
    var low = 0, high = array.length;
    // 不停与中心值对照,寻觅obj的准确插进去点
    while(low < high) {
        // (low + high) >> 1 相当于 Math.floor((low + high) / 2)
        var mid = (low + high) >> 1;
        iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
    }
    // 返回obj插进去array以后的索引号
    return low;
};

这个函数的作用是将obj插进去已排序的array中,返回obj在array中的索引号

_.toArray

_.toArray = function(obj) {
    if(!obj)
        return [];
    if(_.isArray(obj))
        return slice.call(obj);
    // 将arguments转换为数组
    if(_.isArguments(obj))
        return slice.call(obj);
    if(obj.toArray && _.isFunction(obj.toArray))
        return obj.toArray();
    // 将对象转换为数组, 数组中包含对象中一切属性的值列表(不包含对象原型链中的属性)
    return _.values(obj);
};

这个函数很简单,作用是将一个鸠合转换一个数组并返回

_.size

_.size = function(obj) {
    // 假如鸠合是一个数组, 则盘算数组元素数目
    // 假如鸠合是一个对象, 则盘算对象中的属性数目(不包含对象原型链中的属性)
    return _.isArray(obj) ? obj.length : _.keys(obj).length;
};

这个函数用于盘算鸠合中元素的数目,isArray和keys函数后面会引见到

_.first / _.head / _.take

_.first = _.head = _.take = function(array, n, guard) {
    // 假如没有指定参数n, 则返回第一个元素
    // 假如指定了n, 则返回一个新的数组, 包含递次指定数目n个元素
    // guard参数用于肯定只返回第一个元素, 当guard为true时, 指定数目n无效
    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};

这个函数用于返回一个数组的第一个或顺序指定的n个元素

_.initial

_.initial = function(array, n, guard) {
    // 假如没有通报参数n, 则默许返回除末了一个元素外的别的元素
    // 假如通报参数n, 则返回从末了一个元素最先向前的n个元素外的别的元素
    // guard用于肯定只返回一个元素, 当guard为true时, 指定数目n无效
    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};

这个函数返回一个新数组, 包含除末了一个元素外的别的元素, 或消除从末了一个元素最先向前指定n个元素

_.last

_.last = function(array, n, guard) {
    if((n != null) && !guard) {
        // 盘算并指定猎取的元素位置n, 直到数组末端, 作为一个新的数组返回
        return slice.call(array, Math.max(array.length - n, 0));
    } else {
        // 假如没有指定数目, 或guard为true时, 只返回末了一个元素
        return array[array.length - 1];
    }
};

这个函数与first相反,返回数组的末了一个或倒序指定的n个元素

_.rest / _.tail

_.rest = _.tail = function(array, index, guard) {
    // 盘算slice的第二个位置参数, 直到数组末端
    // 假如没有指定index, 或guard值为true, 则返回除第一个元素外的别的元素
    // (index == null)值为true时, 作为参数通报给slice函数将被自动转换为1
    return slice.call(array, (index == null) || guard ? 1 : index);
};

这个函数与initial相反,用于猎取除了第一个或指定前n个元素外的别的元素

_.campact

_.compact = function(array) {
    return _.filter(array, function(value) {
        return !!value;
    });
};

这个函数借助filter函数,返回数组中一切值能被转换为true的元素, 返回一个新的数组,不能被转换的值包含 false, 0, ”, null, undefined, NaN, 这些值将被转换为false

_.flatten

_.flatten = function(array, shallow) {
    // 迭代数组中的每一个元素, 并将返回值作为demo通报给下一次迭代
    return _.reduce(array, function(memo, value) {
        // 假如元素依然是一个数组, 举行以下推断:
        // - 假如不举行深层兼并, 则运用Array.prototype.concat将当前数组和之前的数据举行衔接
        // - 假如支撑深层兼并, 则迭代挪用flatten要领, 直到底层元素不再是数组范例
        if(_.isArray(value))
            return memo.concat( shallow ? value : _.flatten(value));
        // 数据(value)已处于底层, 不再是数组范例, 则将数据兼并到memo中并返回
        memo[memo.length] = value;
        return memo;
    }, []);
};

这个函数用于将一个多维数组合成为一维数组, 支撑深层兼并,个中第二个参数shallow用于掌握兼并深度, 当shallow为true时, 只兼并第一层, 默许举行深层兼并

_.without

_.without = function(array) {
    return _.difference(array, slice.call(arguments, 1));
};

这个函数用于挑选并返回当前数组中与指定数据不相等的差别数据,详细能够参看我后续对difference函数的引见

_.uniq/_.unique

_.uniq = _.unique = function(array, isSorted, iterator) {
    // 假如运用了iterator处置惩罚器, 则先将当前数组中的数据会先经由按迭代器处置惩罚, 并返回一个处置惩罚后的新数组
    // 新数组用于作为比较的基准
    var initial = iterator ? _.map(array, iterator) : array;
    // 用于纪录处置惩罚效果的暂时数组
    var results = [];
    // 假如数组中只要2个值, 则不须要运用include要领举行比较, 将isSorted设置为true能进步运转效力
    if(array.length < 3)
        isSorted = true;
    // 运用reduce要领迭代并累加处置惩罚效果
    // initial变量是须要举行比较的基准数据, 它多是原始数组, 也多是处置惩罚器的效果鸠合(假如设置过iterator)
    _.reduce(initial, function(memo, value, index) {
        // 假如isSorted参数为true, 则直接运用===比较纪录中的末了一个数据
        // 假如isSorted参数为false, 则运用include要领与鸠合中的每一个数据举行对照
        if( isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
            // memo纪录了已比较过的无反复数据
            // 依据iterator参数的状况, memo中纪录的数据多是原始数据, 也多是处置惩罚器处置惩罚后的数据
            memo.push(value);
            // 处置惩罚效果数组中保留的一直为原始数组中的数据
            results.push(array[index]);
        }
        return memo;
    }, []);
    // 返回处置惩罚效果, 它只包含数组中无反复的数据
    return results;
};

这个函数用于对数组中的数据举行去重(运用===举行比较),当isSorted参数不为false时, 将顺次对数组中的元素挪用include要领, 搜检雷同元素是不是已被添加到返回值(数组)中,假如挪用之前确保数组中数据按递次分列, 则能够将isSorted设为true, 它将经由过程与末了一个元素举行对照来消除雷同值, 运用isSorted效力会高于默许的include体式格局,uniq要领默许将以数组中的数据举行对照, 假如声明iterator处置惩罚器, 则会依据处置惩罚器建立一个对照数组, 比较时以该数组中的数据为准, 但终究返回的唯一数据仍然是原始数组

_.union

_.union = function() {
    // union对参数中的多个数组举行浅层兼并为一个数组对象通报给uniq要领举行处置惩罚
    return _.uniq(_.flatten(arguments, true));
};

这个函数与uniq作用一致, 不同之处在于union许可在参数中传入多个数组

_.intersection

_.intersection = _.intersect = function(array) {
    // rest变量纪录须要举行比较的别的数组对象
    var rest = slice.call(arguments, 1);
    // 运用uniq要领去除当前数组中的反复数据, 防止反复盘算
    // 对当前数组的数据经由过程处置惩罚器举行过滤, 并返回相符前提(比较雷同元素)的数据
    return _.filter(_.uniq(array), function(item) {
        // 运用every要领考证每一个数组中都包含了须要对照的数据
        // 假如一切数组中均包含对照数据, 则悉数返回true, 假如恣意一个数组没有包含该元素, 则返回false
        return _.every(rest, function(other) {
            // other参数存储了每一个须要举行对照的数组
            // item存储了当前数组中须要举行对照的数据
            // 运用indexOf要领搜刮数组中是不是存在该元素(可参考indexOf要领解释)
            return _.indexOf(other, item) >= 0;
        });
    });
};

这个函数用于猎取当前数组与别的一个或多个数组的交集元素,从第二个参数最先为须要举行比较的一个或多个数组

_.difference

_.difference = function(array) {
    // 对第2个参数最先的一切参数, 作为一个数组举行兼并(仅兼并第一层, 而并不是深层兼并)
    // rest变量存储考证数据, 在本要领中用于与原数据对照
    var rest = _.flatten(slice.call(arguments, 1), true);
    // 对兼并后的数组数据举行过滤, 过滤前提是当前数组中不包含参数指定的考证数据的内容
    // 将相符过滤前提的数据组合为一个新的数组并返回
    return _.filter(array, function(value) {
        return !_.include(rest, value);
    });
};

这个函数会挑选并返回当前数组中与指定数据不相等的差别数据,平常用于删除数组中指定的数据, 并获得删除后的新数组

小结

本日一共引见了21个函数的详细完成,我都写累了,人人能够也看累了吧,我以为写太多也不利于人人消化这些学问,本日就到这儿吧。thx for reading, hope u enjoy

    原文作者:JoeRay61
    原文地址: https://segmentfault.com/a/1190000000535128
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞