Collection Functions
each & map
_.each = _.forEach = function(obj, iteratee, context) {
// 查看是否有第三个参数
iteratee = optimizeCb(iteratee, context); // iterator
var i, length;
// 如果他有length
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
// iteratee 是用户自定义的回调函数的一个包装
iteratee(obj[i], i, obj);
}
} else {
// 不是arr 就是json,然后取出它的key,然后循环
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
// 返回obj是为了下一次的链式调用
return obj;
};
// value: 每一个参数
// index: 索引
// collection: arr本身
const each = _.each([0, 1, 2, 3, 4], (value, index, collection) => console.log(value));
// 0 -> 1-> 2 -> 3 -> 4
// map 和each差不多
_.map = _.collect = function(obj, iteratee, context) {
// 迭代自身,和each一样,
iteratee = cb(iteratee, context);
// 检查是否是数组,是否含有key
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;
// iteratee同样是用户自定义的函数
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
const map = _.map([0, 1, 2, 3, 4], (value, index, collection) => console.log(value));
// 0 -> 1-> 2 -> 3 -> 4
reduce & reduceRight
// Create a reducing function iterating left or right.
function createReduce(dir) {
// Optimized iterator function as using arguments.length
// in the main function will deoptimize the, see #1991.
function iterator(obj, iteratee, memo, keys, index, length) {
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
// 如果有第三个参数,则memo就是初始值,否则就是obj的第一个参数
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
}
return function(obj, iteratee, memo, context) {
// 迭代
iteratee = optimizeCb(iteratee, context, 4);
// 取出keys length index
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1; // 正序还是倒序
// 如果只有两个参数,判断是数组还是json
// Determine the initial value if none is provided.
// 查看是否给了初始值,
if (arguments.length < 3) {
// 判断是数组还是json并取值。
memo = obj[keys ? keys[index] : index];
// 获取初始index > 如果从左开始,index = 1 ,否则等于length -1
index += dir;
}
return iterator(obj, iteratee, memo, keys, index, length);
};
}
// **Reduce** builds up a single result from a list of values, aka `inject`, or `foldl`.
_.reduce = _.foldl = _.inject = createReduce(1);
// The right-associative version of reduce, also known as `foldr`.
_.reduceRight = _.foldr = createReduce(-1);
// 两个参数的时候,累加数组,当有第三个参数时,则是将第三个参数当做初始值
var reduceRight = _.reduceRight(
[0, 1, 2, 3, 4],
function(memo, value , index, list){
return memo + value
}, 10);
console.log(reduceRight);
// 20
find
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var key;
if (isArrayLike(obj)) {
key = _.findIndex(obj, predicate, context);
} else {
key = _.findKey(obj, predicate, context);
}
if (key !== void 0 && key !== -1) return obj[key];
};
filter and reject
// Return all the elements that pass a truth test.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
_.each(obj, function(value, index, list) {
// 便利obj 的每一个值,然后返回predicate为真的值
if (predicate(value, index, list)) results.push(value);
});
return results;
};
// 取predicate为真的值
var filter = _.filter([2, 0, 1, 3, 4], function(value, index, list) {
return value != index
});
console.log(filter);
// [2, 0, 1]
// 取一个表达式的否定值
// Returns a negated version of the passed-in predicate.
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
// 将filter的第二个值设为否定值,则每次返回的都是取反
// reject: 拒绝
// negate: 否定 a => !a
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
return _.filter(obj, _.negate(cb(predicate)), context);
};
// 取predicate为真的值
var reject = _.reject([2, 0, 1, 3, 4], function(value, index, list){return value != index});
console.log(reject);
// [3, 4]
every and some
/**
* Determine: 确定
* whether: 是否
* match: 使相配/相同
* truth test: 真理 检验
* Aliased: 别名
*/
// Determine whether all of the elements match a truth test. Aliased as `all`.
// 所有内容都必须符合条件,否则为假
_.every = _.all = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj), // arr、json
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;
};
var every = _.every([0, 1, 2, 3], (value, key, list) => value < 3)
console.log(every)
// false
// Determine if at least one element in the object matches a truth test. Aliased as `any`.
// 只要有一个符合条件,则为真
_.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;
};
var sume = _.some([10, 9, 5, 7], (value, key, list) => value < 7)
console.log(sume)
// true
contains
// Determine if the array or object contains a given value (using `===`).
// Aliased as `includes` and `include`.
// 目标内容是否存在 fromIndex 是从什么位置开始查找 包括位置自身
_.contains = _.includes = _.include = function(obj, target, fromIndex) {
// 如果股市数组,就把里面的值全部取出来,变相的变成数组
if (!isArrayLike(obj)) obj = _.values(obj);
// obj = ['aaa', 'bbb']
return _.indexOf(obj, target, typeof fromIndex == 'number' && fromIndex) >= 0;
};
var sumc = _.contains({a: 'aaa', b: 'bbb'}, 'bbb', 1); // 1 是起始位置 是_.indexOf()的第三个参数
console.log(sumc);
// true
/*——————————————————————————————————————————————————————————————————————————*/
/**
* invoke
*/
// Invoke a method (with arguments) on every item in a collection.
// 在list的每个元素上执行methodName方法。 任何传递给invoke的额外参数,invoke都会在调用methodName方法的时候传递给它。
// 目标内容是否存在 fromIndex 是从什么位置开始查找 包括位置自身
_.invoke = function(obj, method) {
//invoke方法接受多于两个参数作为函数参数,从第3个参数开始将作为被调用函数的参数
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
// 这里判断用户定义的是方法,还是方法名
var func = isFunc ? method : value[method];
return func == null ? func : func.apply(value, args);
});
};
console.log( _.invoke([[5, 1, 7], [3, 2, 1]], 'reverse'));
// [[7, 1, 5], [1, 2, 3]]
// splice 可以传参数,则放在后面
var sums = _.invoke([[5, 1, 7], [3, 2, 1]], 'splice', 2);
console.log(sums);
// [[7], [1]]
/*——————————————————————————————————————————————————————————————————————————*/
/**
* pluck
* 也许是map最常使用的用例模型的简化版本,即萃取数组对象中某属性值,返回一个数组。
*/
// 返回obj的每一个key的值
_.property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
// 萃取json模型对象中的某个特定值
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
// 发现每次看到这种return 函数的方式都有点梦,然后把它扔回去,瞬间感觉好理解多了。
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
/*——————————————————————————————————————————————————————————————————————————*/
/**
* where and findWhere
* 根据属性查找obj中的位置
*/
_.matcher = _.matches = function(attrs) {
// 将attrs 合并到{}
attrs = _.extendOwn({}, attrs);
return function(obj) {
return _.isMatch(obj, attrs);
};
};
// 告诉你attrs中的键和值是否包含在object中。
_.isMatch = function(object, attrs) {
var keys = _.keys(attrs), length = keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = keys[i];
// 两个的键值对不相等,或者obj中没有这个key ,则返回false
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs) {
return _.filter(obj, _.matcher(attrs));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.find(obj, _.matcher(attrs));
};
var stges = [
{name: 'moe', age: 40, height: 56, size: 23},
{name: 'larry', age: 50},
{name: 'moe', age: 40, family: 'english'},
{name: 'curly', age: 60}
];
var cum = _.where(stges, {name: "moe", age: 40});
console.log(cum);
// [{name: 'moe', age: 40, height: 56, size: 23},{name: 'moe', age: 40, family: 'english'}]
var tom = _.findWhere(stges, {name: "moe", age: 40});
console.log(tom);
// [{name: 'moe', age: 40, height: 56, size: 23}]
/*——————————————————————————————————————————————————————————————————————————*/
/**
* max and min
*/
// 根据比较规则,取无穷大。
// Return the maximum element (or element-based computation).
_.max = function(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
value, computed;
if (iteratee == null && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
// 如果value大于result 则让result = value ,然后再与下一个比较
if (value > result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(value, index, list) {
// computed 是比较函数返回的值,如果比lastComputed 大,则将值赋给lastComputed,然后在于下一个比较
computed = iteratee(value, index, list);
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
result = value;
lastComputed = computed;
}
});
}
return result;
};
var stooges = [
{name: 'moe', age: 40, height: 56, size: 23},
{name: 'larry', age: 50},
{name: 'moe', age: 40, family: 'english'},
{name: 'curly', age: 60}
];
var sum = _.max(stooges, function(obj){
return obj.age
});
console.log(sum);
// Return the minimum element (or element-based computation).
// 和max差不多
_.min = function(obj, iteratee, context) {
var result = Infinity, lastComputed = Infinity,
value, computed;
if (iteratee == null && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value < result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(value, index, list) {
computed = iteratee(value, index, list);
if (computed < lastComputed || computed === Infinity && result === Infinity) {
result = value;
lastComputed = computed;
}
});
}
return result;
};
/*——————————————————————————————————————————————————————————————————————————*/
/**
* shuffle
* 输出一个随机排序的函数
*/
// Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) {
var set = isArrayLike(obj) ? obj : _.values(obj);
var length = set.length;
var shuffled = Array(length);
for (var index = 0, rand; index < length; index++) {
// 返回一个0 到index 之间的随机数
rand = _.random(0, index);
// 如果随机数不等一index ,则将shuffled就等于shuffled的第rand个
// 比如说第五次,index = 5, rand 为 3
// shuffled[5] = shuffled[3]
// shuffled[3] = set[5]
// 算是交换了个一个值,并且消除了重复的值
// 使用了Fisher-Yates 乱序算法
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = set[index];
}
return shuffled;
};
/*——————————————————————————————————————————————————————————————————————————*/
/**
* sample
* 输出一个或多个随机值
*/
// Sample **n** random values from a collection.
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
// 如果n 不存在,就返回一个随机值
if (n == null || guard) {
// 如果不是arry,就将obj装换为数组
if (!isArrayLike(obj)) obj = _.values(obj);
// 取一个随机值
return obj[_.random(obj.length - 1)];
}
// 在使用乱序算法的基础上,使用slice方法获取n个
return _.shuffle(obj).slice(0, Math.max(0, n));
};
/*——————————————————————————————————————————————————————————————————————————*/
/**
* sortBy
* 排序
*/
// 按照特定的方式排序
// Sort the object's values by a criterion produced by an iteratee.
_.sortBy = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
// 先用pluck 取出value的值,然后使用iteratee排序
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
// iteratee如果是function 则执行func, 如果不存在则返回value本身
// 可以查看cd()函数,value == null => _.identity;
criteria: iteratee(value, index, list)
};
}).sort(function(left, right) {
/// 排序
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1; // 大于
if (a < b || b === void 0) return -1; // 小于
}
return left.index - right.index; // 等于
// 最后是使用pluck 的方法,将value的值输出出来。
}), 'value');
};
/*——————————————————————————————————————————————————————————————————————————*/
/**
* groupBy & indexBy & countBy
* 拆分集合
*/
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
return function(obj, iteratee, context) {
var result = {};
iteratee = cb(iteratee, context);
_.each(obj, function(value, index) {
// 调用自定义的方法返回一个key来拆分集合
var key = iteratee(value, index, obj);
// 定义不同的输出结果
behavior(result, value, key);
});
return result;
};
};
// 把一个集合分组为多个集合,通过自定义函数返回的结果进行分组. 如果自定义函数是一个字符串而不是函数, 那么将使用 iterator 作为各元素的属性名来对比进行分组.
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, value, key) {
// 如果存在,就将key作为键,如果没有,就将每一个都push到这个key里面
if (_.has(result, key)) result[key].push(value); else result[key] = [value];
});
//和groupBy非常像,但是当你知道你的键是唯一的时候可以使用indexBy 。
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, value, key) {
// 使用key来拆分,并把key作为每一项的key
result[key] = value;
});
// 类似于indexby,但是不返回列表的值,只返回结果的个数
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, value, key) {
if (_.has(result, key)) result[key]++; else result[key] = 1;
});
/*——————————————————————————————————————————————————————————————————————————*/
/**
* toArray
* 转成array
*/
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];// 确定存在
if (_.isArray(obj)) return slice.call(obj); // 如果是一个一个arry,则用slice拆分
if (isArrayLike(obj)) return _.map(obj, _.identity); //如果有length,用map重新返回自身
return _.values(obj); //如果是json,则取出每一个值
};
/*——————————————————————————————————————————————————————————————————————————*/
/**
* size
* 返回集合的长度
*/
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
// 如果有length属性,则直接输出length,如果是json,则取出所有的key,然后求length
return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};
/*——————————————————————————————————————————————————————————————————————————*/
/**
* partition
* 返回两个集合,一个是符合的,一个是不符合的。
*/
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(obj, predicate, context) {
predicate = cb(predicate, context);
var pass = [], fail = [];
_.each(obj, function(value, key, obj) {
// (predicate(value, key, obj) 为true 则pass.push 否则 fail.push
(predicate(value, key, obj) ? pass : fail).push(value);
});
return [pass, fail];
};