资讯专栏INFORMATION COLUMN

Underscore源码解析(二)

骞讳护 / 2584人阅读

摘要:本文同步自我得博客最近十几天都在忙毕业论文的事,所以上一次为大家介绍完这个框架的结构或者说是这个框架的设计思路之后就一直没动静了,今天我又满血复活了,让我们继续来探索的源码奥秘吧。

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

最近十几天都在忙毕业论文的事,所以上一次为大家介绍完underscore这个框架的结构(或者说是这个框架的设计思路)之后就一直没动静了,今天我又满血复活了,让我们继续来探索underscore的源码奥秘吧。
没看过上一篇文章的朋友可以戳这里:underscore源码解析(一)
今天的内容是underscore里面封装的一些函数,我将逐个介绍,咱们直接入正题吧

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会存储每次迭代的返回值, 并作为一个新的数组返回

_.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相似,不过它是逆向迭代集合中的元素

_.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作用类似, 但它会记录下集合中所有通过验证的元素

_.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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78161.html

相关文章

  • Underscore源码解析(四)

    摘要:本文同步自我得博客我在这个系列的第一篇文章说过,我学是为了在学的时候少一些阻碍,从第一篇的写作时间到今天,大概也有个十几二十天,感觉拖得有点久,所以今天将会是源码解析系列的最后一篇文章,我会在这篇文章中介绍剩下的所有函数。 本文同步自我得博客:http://www.joeray61.com 我在这个系列的第一篇文章说过,我学underscore是为了在学backbone的时候少一些阻碍...

    高胜山 评论0 收藏0
  • Underscore源码解析(一)

    摘要:本文同步自我得博客最近准备折腾一下,在事先了解了之后,我知道了对这个库有着强依赖,正好之前也没使用过,于是我就想先把彻底了解一下,这样之后折腾的时候也少一点阻碍。 本文同步自我得博客:http://www.joeray61.com 最近准备折腾一下backbone.js,在事先了解了backbone之后,我知道了backbone对underscore这个库有着强依赖,正好undersc...

    neu 评论0 收藏0
  • Underscore源码解析(三)

    摘要:本文同步自我得博客前两天在微博上看到的微博推荐了我的前两篇文章,有点意外和惊喜。没看过前两篇博客的朋友可以戳这里源码解析一源码解析二上一篇文章介绍了的个函数的具体实现细节,今天将继续介绍其他的函数。 本文同步自我得博客:http://www.joeray61.com 前两天在微博上看到SF的微博推荐了我的前两篇文章,有点意外和惊喜。作为一个菜鸟,真的是倍受鼓舞,我写博客的动力也更充足了...

    Prasanta 评论0 收藏0
  • underscore源码解析

    说明1、源码结构通览,简单注释说明2、通过调用方法讲解核心代码逻辑 一、源码的结构 为了方便比对源码,按源码的结构顺序展示。underscore是个轻量级的工具库,大部分代码是实现特定功能以函数的形式存在,本身会比较简单,没对方法具体说明,可直接参考underscore中文文档 (function() { var root = this; var previousUnderscore = ...

    kid143 评论0 收藏0
  • Underscore源码中文注释(转)

    摘要:创建一个全局对象在浏览器中表示为对象在中表示对象保存下划线变量被覆盖之前的值如果出现命名冲突或考虑到规范可通过方法恢复被占用之前的值并返回对象以便重新命名创建一个空的对象常量便于内部共享使用将内置对象的原型链缓存在局部变量方便快速调用将 // Underscore.js 1.3.3 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc....

    Guakin_Huang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<