underscore.js源码解析(三)
最近工作比较忙,做不到每周两篇了,周末赶着写吧,上篇我针对一些方法进行了分析,今天继续。
没看过前两篇的可以猛戳这里:
underscore.js源码GitHub地址: https://github.com/jashkenas/underscore/blob/master/underscore.js本文解析的underscore.js版本是1.8.3
_.map/_.collect
_.map = _.collect = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
//判断是否是数组,是的话就将里面的属性取出
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
map和each的区别就是map是将最后的结果以数组的形式返回
createReduce
var createReduce = function(dir) {
var reducer = function(obj, iteratee, memo, initial) {
//判断是否是数组
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
//判断迭代方向,dir为1是从左向右迭代,dir为-1是从右向左迭代(从_.reduce和_.reduceRight里就可以清晰的看出来)
index = dir > 0 ? 0 : length - 1;
//判断是否存在初始值
if (!initial) {
//如果没有初始值,则将第一个设置为初始值,前面判断了是否是数组,是数组返回数组否则返回对象
memo = obj[keys ? keys[index] : index];
//根据方向将初始位置赋给index,为了下边遍历使用
index += dir;
}
//进行遍历
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
//进行迭代运算
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};
return function(obj, iteratee, memo, context) {
//通过参数数量判断是否有初始值
var initial = arguments.length >= 3;
//调用reducer()
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
};
看了上面的注释你可以清晰的发现createReduce的结构已经很清晰了,内部调用reducer函数,先判断有没有初始值,如果没有则根据迭代方向给初始值赋值,然后进行循环的迭代。
createPredicateIndexFinder
var createPredicateIndexFinder = function(dir) {
return function(array, predicate, context) {
//迭代函数
predicate = cb(predicate, context);
var length = getLength(array);
//判断遍历方向,dir为1是从左向右,dir为-1是从右向左
var index = dir > 0 ? 0 : length - 1;
for (; index >= 0 && index < length; index += dir) {
//遍历返回符合条件的是第几个
if (predicate(array[index], index, array)) return index;
}
//否则返回-1
return -1;
};
};
_.findIndex/_.findLastIndex
_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);
调用上面的createPredicateIndexFinder内部函数,两者只是遍历的方向不同,最终返回相应的索引
_.findKey
_.findKey = function(obj, predicate, context) {
//迭代函数
predicate = cb(predicate, context);
var keys = _.keys(obj), key;
for (var i = 0, length = keys.length; i < length; i++) {
//获取对象属性
key = keys[i];
if (predicate(obj[key], key, obj)) return key;
}
};
返回满足条件的属性
_.find / _.detect
_.find = _.detect = function(obj, predicate, context) {
var key;
if (isArrayLike(obj)) {
//如果是数组通过findIndex获取满足条件的索引
key = _.findIndex(obj, predicate, context);
} else {
//如果是对象则通过findKey获取满足条件的属性
key = _.findKey(obj, predicate, context);
}
//根据上面取到的索引或属性,返回相应的值
if (key !== void 0 && key !== -1) return obj[key];
};
_.filter/_.select
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
//遍历数据,将符合条件的存入results数组当中,并返回
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
_.negate
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
_.negate就是一个取反的函数
_.reject
_.reject = function(obj, predicate, context) {
return _.filter(obj, _.negate(cb(predicate)), context);
};
_.every/_.all
_.every = _.all = function(obj, predicate, context) {
predicate = cb(predicate, context);
//判断数组还是对象
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
//数组获取索引,对象获取属性
var currentKey = keys ? keys[index] : index;
//如果有不满足条件的就返回false
if (!predicate(obj[currentKey], currentKey, obj)) return false;
}
return true;
};
_.every就是判断是否所有的数据都满足条件
_.some
_.some = _.any = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
if (predicate(obj[currentKey], currentKey, obj)) return true;
}
return false;
};
跟every的判断结构差不多,只不过最后的判断条件不同,some是判断时候有满足条件的,有的话就返回true
_.values
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
_.values就是讲obj的所有值拷贝到数组当中
_.sortedIndex
_.sortedIndex = function(array, obj, iteratee, context) {
iteratee = cb(iteratee, context, 1);
var value = iteratee(obj);
var low = 0, high = getLength(array);
while (low < high) {
var mid = Math.floor((low + high) / 2);
if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
}
return low;
};
_.sortedIndex看过源码就可以看出是二分法查找
createIndexFinder
var createIndexFinder = function(dir, predicateFind, sortedIndex) {
return function(array, item, idx) {
var i = 0, length = getLength(array);
if (typeof idx == 'number') {
//判断方向,1是从前到后,-1则为从后到前
if (dir > 0) {
i = idx >= 0 ? idx : Math.max(idx + length, i);
} else {
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
}
//如果是排序好的就使用二分法
} else if (sortedIndex && idx && length) {
idx = sortedIndex(array, item);
//判断找出的值是否一样,是就返回这个值,否则返回-1
return array[idx] === item ? idx : -1;
}
if (item !== item) {
//对item为NaN的处理
idx = predicateFind(slice.call(array, i, length), _.isNaN);
return idx >= 0 ? idx + i : -1;
}
for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
//通过遍历的方法找出item对应的索引
if (array[idx] === item) return idx;
}
//找不到则返回-1
return -1;
};
};
_.indexOf/_.lastIndexOf
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
_.contains/_.includes/_.include
//判断是否包含对应的值
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
//如果是对象,则将obj的所有值拷贝到数组当中
if (!isArrayLike(obj)) obj = _.values(obj);
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
//查找是否存在这个值,如果存在,indexOf 返回相应的索引,则为true,如果不存在,indexOf 返回-1,则为false
return _.indexOf(obj, item, fromIndex) >= 0;
};
_.invoke
//对每一个元素都执行一次方法,最后把结果存入数组返回
_.invoke = restArgs(function(obj, method, args) {
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
//如果是函数则每个元素都执行一遍方法,如果不是,则返回所有的值,最后结果以数组的形式返回
var func = isFunc ? method : value[method];
return func == null ? func : func.apply(value, args);
});
});
小结
今天分析的几个内部函数理解起来有点难度,理解要多看几遍分析不同的情况,也许结合调用的例子理解起来会容易一点,分析我都写在了代码间的注释里,这样读起源码会容易一点,今天就写到这里吧,有点累了,明天还要上班,剩下的方法在之后的文章里都会分析到。
感谢大家的观看,也希望能够和大家互相交流学习,有什么分析的不对的地方欢迎大家批评指出
参考资料
https://segmentfault.com/a/1190000000531871 http://www.w3cfuns.com/house/17398/note/class/id/bb6dc3cabae6651b94f69bbd562ff370underscore.js源码解析(三)的更多相关文章
- underscore.js源码解析(五)—— 完结篇
最近公司各种上线,所以回家略感疲惫就懒得写了,这次我准备把剩下的所有方法全部分析完,可能篇幅过长...那么废话不多说让我们进入正题. 没看过前几篇的可以猛戳这里: underscore.js源码解析( ...
- underscore.js源码解析(四)
没看过前几篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二) underscore.js源码解析(三) underscore.js源码GitHub地 ...
- underscore.js源码解析(二)
前几天我对underscore.js的整体结构做了分析,今天我将针对underscore封装的方法进行具体的分析,代码的一些解释都写在了注释里,那么废话不多说进入今天的正文. 没看过上一篇的可以猛戳这 ...
- underscore.js源码解析(一)
一直想针对一个框架的源码好好的学习一下编程思想和技巧,提高一下自己的水平,但是看过一些框架的源码,都感觉看的莫名其妙,看不太懂,最后找到这个underscore.js由于这个比较简短,一千多行,而且读 ...
- underscore.js源码解析【'_'对象定义及内部函数】
(function() { // Baseline setup // -------------- // Establish the root object, `window` (`self`) in ...
- underscore.js源码解析【对象】
// Object Functions // ---------------- // Keys in IE < 9 that won't be iterated by `for key in . ...
- underscore.js源码解析【数组】
// Array Functions // --------------- // Get the first element of an array. Passing **n** will retur ...
- underscore.js源码解析【集合】
// Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `f ...
- underscore.js源码解析【函数】
// Function (ahem) Functions // ------------------ // Determines whether to execute a function as a ...
随机推荐
- StackStorm利用CORS null origin获得RCE (CVE-2019-9580)
在2.10.3/2.9.3之前,如果请求的来源未知,我们将返回null,null可以导致某些客户端中来自未知来源的成功请求,允许针对StackStorm API进行XSS样式攻击. (Firefox上 ...
- java核心技术-多线程之线程内存模型
对于每一种编程语言,理解它的内存模型是理所当然的重要.下面我们从jvm的内存模型来体会下java(不限java语言,严格来讲是JVM内存模型,所有JVM体系的变成语言均适用)的内存模型. 堆: 就是我 ...
- HCDA day1
OSI有几层: OSI将计算机网络体系结构(architecture)划分为以下七层: 图1.OSI模型 物理层: 将数据转换为可通过物理介质传送的电子信号 相当于邮局中的搬运工人. 物理层(Phys ...
- 从Windows复制文件到Linux
PuTTY自带的pscp非常好用. 使用方式和Linux中的scp命令很像,格式如下: pscp 文件 用户名@LinuxIP:目录 例如: pscp d:/a.iso root@192.168.1. ...
- vim插件管理 - vim-plug
vim-plug是一款轻量的vim插件管理工具. GitHub:https://github.com/junegunn/vim-plug 插件的安装 unix curl -fLo ~/.vim/aut ...
- FPGA时序逻辑中常见的几类延时与时间(五)
FPGA逻辑代码重要的是理解其中的时序逻辑,延时与各种时间的记忆也是一件头疼的事,这里把我最近看到的比较简单的几类总结起来,共同学习. 一.平均传输延时 平均传输延时 二.开启时间与关闭时间 开 ...
- 《Java核心技术36讲》阅读笔记:Exception和Error有什么区别?
1.Exception 和 Error有什么区别?运行时异常与一般异常有什么区别? Exception和Error都继承自java.lang.Throwable.在Java中只有Throwable的实 ...
- 20155215 2006-2007-2 《Java程序设计》第2周学习总结
20155215 2006-2007-2 <Java程序设计>第2周学习总结 教材学习内容总结 第三章主要讲述了JAVA程序编写中的一些基本语法.其实看了第三章之后我就感觉到,C语言不愧是 ...
- 【BZOJ4008】[HNOI2015]亚瑟王
[BZOJ4008][HNOI2015]亚瑟王 题面 bzoj 洛谷 题解 由期望的线性性 可以知道,把所有牌打出的概率乘上它的伤害加起来就是答案 记第$i$张牌打出的概率为$fp[i]$ 则 $$ ...
- Yii 2.0 中事件的使用
关于PHP的事件处理,参照 http://www.cnblogs.com/mafeifan/p/4322238.html http://www.cnblogs.com/mafeifan/p/43222 ...