Underscore 集合
集合(Collections)
1. _.each
.each(list, iterator, [context])(.forEach) 遍历list中的所有元素,按顺序用遍历输出每个元素。如果传递了context参数,则把iteratee绑定到context对象上。每次调用iteratee都会传递三个参数:(element, index, list)。如果list是个JavaScript对象,iteratee的参数是 (value, key, list))。返回list以方便链式调用。(愚人码头注:如果存在原生的forEach方法,Underscore就使用它代替。)
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
// _.keys(object) 检索object拥有的所有可枚举属性的名称。返回数组
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
1、 首先执行“optimizeCb()”函数,得到迭代器iteratee的值。 语句:
iteratee = optimizeCb(iteratee, context);
即如下图函数所示,argCount没有传入参数,即选择case 3. iteratee得到的值为:
iteratee = function(value, index, collection) {
return func.call(context, value, index, collection);
};
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
// call函数和apply方法的第一个参数都是要传入给当前对象的对象,及函数内部的this。
// 后面的参数都是传递给当前对象的参数。
// 对于apply和call两者在作用上是相同的,但两者在参数上有区别的。
// 对于第一个参数意义都一样,但对第二个参数:
// apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,
// 而call则作为call的参数传入(从第二个参数开始)。
// 如 func.call(func1,var1,var2,var3)
// 对应的apply写法为:func.apply(func1,[var1,var2,var3])
// 同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入
2、 判断传入的对象obj是否为类数组对象或对象,类数组对象是有一个数值length属性和对应非负整数属性的对象,如:{‘0’:’a’, ‘1’:’b’, ‘2’:’c’, length:3}。语句:
if (isArrayLike(obj))
即如下图函数所示,使用代码:
length= property('length')(collection)
判断length是否有值。
var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
// Math.pow(底数,几次方)
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = property('length');
var isArrayLike = function(collection) {
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
例:
var a = [1,2,3];
var b = {
one:'one1',
two:'two2',
three:'three3'
};
console.log(a["length"]); //3
console.log(b["length"]); //undefined
1) 若obj为数组,则使用for循环,遍历从obj数组的第0为开始到,到obj的最后一位结束,并求出三个数值:obj[i](obj数组对应的值)、i(obj数组对应的索引位置)、obj(obj数组列表本身),传进迭代器iteratee中,并运行iteratee函数。
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
例:
var a = [1,2,3];
var text1 = _.each(a,function (element,index,list){
console.log(element+','+index);
console.log(list);
});
/*
1,0
[1,2,3]
2,1
[1,2,3]
3,2
[1,2,3]
*/
2) 若obj为对象
a. 首先,检索object拥有的所有可枚举属性的名称,并返回数组keys。
var keys = _.keys(obj);
b. 使用for循环,遍历keys并返回它们的值(obj的属性),并求出三个值:obj[keys[i]](obj对象属性对应的值)、keys[i](obj对象对应的属性名称)、obj(obj对象列表本身),传进迭代器iteratee中,并运行iteratee函数。
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
例:
var b = {
one:'one1',
two:'two2',
three:'three3'
};
var text2 = _.each(b,function (element,index,list){
console.log(element+','+index);
console.log(list);
});
/*
one1,one
Object {one: "one1", two: "two2", three: "three3"}
two2,two
Object {one: "one1", two: "two2", three: "three3"}
three3,three
Object {one: "one1", two: "two2", three: "three3"}
*/
console.log(text2);//Object {one: "one1", two: "two2", three: "three3"}
3、 最后返回obj本身return obj;
2. _.map
.map(list, iterator, [context])(. collect) 通过转换函数(iterator迭代器)映射列表中的每个值产生价值的新数组。iterator传递三个参数:value,然后是迭代 index(或 key 愚人码头注:如果list是个JavaScript对象是,这个参数就是key),最后一个是引用指向整个list。
_.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;
};
1、 首先执行“cb()”函数,得到迭代器iteratee的值。语句:
iteratee = cb(iteratee, context);
即如下图函数所示:
var builtinIteratee;
var cb = function(value, context, argCount) {
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
// _.identity(value) 返回与传入参数相等的值. 相当于数学里的: f(x) = x,
// 这个函数看似无用, 但是在Underscore里被用作默认的迭代器iterator.
if (value == null) return _.identity;
// _.isFunction(object) 如果object是一个函数(Function),返回true。
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
// _.isObject(value) 如果object是一个对象,返回true。
// 需要注意的是JavaScript数组和函数是对象,字符串和数字不是。
// _.matcher(attrs) 返回一个断言函数,(matcher:匹配器)
// 这个函数会给你一个断言可以用来辨别给定的对象是否匹配attrs指定键/值属性。
if (_.isObject(value)) return _.matcher(value);
// _.property(key) 返回一个函数,这个函数返回任何传入的对象的key属性。
return _.property(value);
};
1) 若value为空值
返回与传入参数value相等的值 _.identity(value) 相当于数学里的: f(x) = x。
// 保持恒等函数默认迭代。
_.identity = function(value) {
return value;
};
2) 若value为函数
运行函数“optimizeCb()”函数,得到迭代器iteratee的值。 argCount没有传入参数,即选择case 3. iteratee得到的值为:
iteratee = function(value, index, collection) {
return func.call(context, value, index, collection);
};
3) 若value为对象(数组或对象)
则返回一个函数(断言函数),这个函数会给你一个断言可以用来辨别给定的对象是否匹配value(attrs)指定键/值属性.
_.matcher = _.matches = function(attrs) {
// _.extendOwn(destination, *sources) Alias: assign
// 类似于 extend, 但复制source对象中自己的属性覆盖到destination目标对象。
// 并且返回 destination 对象. 复制是按顺序的,
// 所以后面的对象属性会把前面的对象属性覆盖掉(如果有重复).
// (愚人码头注:不包括继承过来的属性)
attrs = _.extendOwn({}, attrs);
return function(obj) {
// _.isMatch(object, properties)
// 告诉你properties中的键和值是否包含在object中。
return _.isMatch(obj, attrs);
};
};
4) 若value为其他值(数字,字符串,undefined,布尔值),则返回一个函数,这个函数返回任何传入的对象的value属性值
obj(value):_.property= property (value);
var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
_.property = property;
2、 判断obj是不是类数组对象,检索object拥有的所有可枚举属性的名称,并返回数组keys。
3、 使用for循环,遍历keys(index)并返回它们的值(obj的属性或数组值),并求出三个值:obj[keys[i]/i](obj对象属性(数组索引)对应的值)、keys[i]/i(obj对象对应的属性名称(数组对应的索引))、obj(obj对象(数组)列表本身),传进迭代器iteratee中,并运行iteratee函数后,返回新的数组列表值。
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
例:
var a = [1,2,3];
var test1 = _.map(a,function (element,index,list){
console.log(element+','+index);
console.log(list);
return element*3;
});
/*
1,0
[1,2,3]
2,1
[1,2,3]
3,2
[1,2,3]
*/
console.log(test1);//[3, 6, 9]
var b = {
one:'one1',
two:'two2',
three:'three3'
};
var test2 = _.map(b,function (element,key,list){
console.log(element+','+key);
console.log(list);
return element+':'+key;
});
/*
one1,one
Object {one: "one1", two: "two2", three: "three3"}
two2,two
Object {one: "one1", two: "two2", three: "three3"}
three3,three
Object {one: "one1", two: "two2", three: "three3"}
*/
console.log(test2);
//["one1:one", "two2:two", "three3:three"]
3. .reduce和.reduceRight
.reduce(list, iterator, [memo], [context]) (.foldl/.inject) _.reduceRight(list, iteratee, memo, [context]) (.foldr) Memo(备忘录)是reduce函数的初始值,reduce的每一步都需要由iterator返回。这个迭代传递4个参数:memo,value 和 迭代的index(或者 key)和最后一个引用的整个 list,最后一个是引用指向整个list。如果reduce的初始调用没有传递memo,iterator将会调用列表中的第一个元素。第一个元素通过memo参数在iterator中调用并传递到列表的下一个元素中。 reducRight是从右侧开始组合的元素的reduce函数,如果存在JavaScript 1.8版本的reduceRight,则用其代替。Foldr在javascript中不像其它有懒计算的语言那么有用(愚人码头注:lazy evaluation:一种求值策略,只有当表达式的值真正需要时才对表达式进行计算)。
_.reduce = _.foldl = _.inject = createReduce(1);
_.reduceRight = _.foldr = createReduce(-1);
var createReduce = function(dir) {
var reducer = function(obj, iteratee, memo, initial) {
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if (!initial) {
memo = obj[keys ? keys[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;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
};
1、 判断是否有传memo的值,并得到initial的布尔值。
2、 运行optimizeCb()函数,且argCount传入的参数值为4,故: iteratee得到的值为:
iteratee = function(accumulator,value, index, collection) {
return func.call(context, accumulator ,value, index, collection);
};
3、 运行reducer()函数,并得到最新的memo值,并返回
例:
var a = [1,2,3,4];
var test1 = _.reduce(a,function (memo,num,index,list){
console.log(memo+','+num+','+index);
console.log(list);
return memo + num;
}, 3);
/*
3,1,0
[1, 2, 3, 4]
4,2,1
[1, 2, 3, 4]
6,3,2
[1, 2, 3, 4]
9,4,3
[1, 2, 3, 4]
*/
console.log(test1);//13
var b = [1,2,3,4];
var test2 = _.reduce(b,function (memo,num,index,list){
console.log(memo+','+num+','+index);
console.log(list);
return num ;
}, 3);
/*
3,1,0
[1, 2, 3, 4]
1,2,1
[1, 2, 3, 4]
2,3,2
[1, 2, 3, 4]
3,4,3
[1, 2, 3, 4]
*/
console.log(test2);//4
var c = {
one:'a',
two:'b',
three:'c',
};
var test3 = _.reduce(c,function (memo,value,key,list){
console.log(value+','+key);
console.log(memo);
return value ;
}, 'd');
/*
a,one
d
b,two
a
c,three
b
*/
console.log(test3);//c
var list2 = [1,2,3,4];
var test2 = _.reduceRight(list2,function (memo,data,rightIndex,list){
console.log( memo );
console.log( data );
console.log( rightIndex );
console.log( list );
return memo.concat(data);
}, [4]);
/*
[4]
4
3
[1, 2, 3, 4]
[4, 4]
3
2
[1, 2, 3, 4]
[4, 4, 3]
2
1
[1, 2, 3, 4]
[4, 4, 3, 2]
1
0
[1, 2, 3, 4]
*/
console.log(test2);// [4, 4, 3, 2, 1]
4. _.find
.find(list, predicate, [context])(.detect) 在list中逐项查找,返回第一个通过predicate迭代函数真值检测的元素值,如果没有值传递给测试迭代器将返回undefined,如果找到匹配的元素,函数将立即返回,不会遍历整个list。
_.find = _.detect = function(obj, predicate, context) {
var key;
if (isArrayLike(obj)) {
// _.findIndex(array, predicate, [context])
// 类似于_.indexOf,当predicate通过真检查时,返回第一个索引值;否则返回-1。
key = _.findIndex(obj, predicate, context);
} else {
// _.findKey(object, predicate, [context])
// 类似于_.findIndex,但是检测objects的属性,当predicate通过真检查时,返回属性名称;否则返回-1。
key = _.findKey(obj, predicate, context);
}
if (key !== void 0 && key !== -1) return obj[key];
};
1、于 _.indexOf,当predicate通过真检查时,返回第一个索引值;否则返回-1。
2、 若obj为对象,则执行函数 _.findKey(),此函数类似于 _.findIndex,但是检测objects的属性,当predicate通过真检查时,返回属性名称;否则返回undefined。
3、 若key存在且不等于-1,则返回对象中当前key(属性或索引)的值
4、 其中:
1) _.findIndex()
_.findIndex = createPredicateIndexFinder(1);
var createPredicateIndexFinder = function(dir) {
return function(array, predicate, context) {
predicate = cb(predicate, context);
var length = getLength(array);
var index = dir > 0 ? 0 : length - 1;
for (; index >= 0 && index < length; index += dir) {
if (predicate(array[index], index, array)) return index;
}
return -1;
};
};
a. 运行cb()函数,得到迭代函数predicate()的值
predicate=optimizeCb(predicate,context);
=function(value, index, collection) {
return predicate.call(context, value, index, collection);
};
b. index从0开始进行for循环,当满足迭代函数predicate的条件时,直接返回index(索引位置),若遍历完列表后,没有满足迭代函数predicate条件,则返回-1
2) _.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;
}
};
a. 运行cb()函数,得到predicate的值
predicate=optimizeCb(predicate,context);
=function(value, index, collection) {
return predicate.call(context, value, index, collection);
};
b. 检索object拥有的所有可枚举属性的名称,并返回数组keys。
c. index从0开始进行for循环,遍历obj的各个属性,并把属性名称值赋给key,当满足迭代函数predicate的条件时,直接返回key(属性名称值),若遍历完列表后,没有满足迭代函数predicate条件,则返回undefined。 例:
var list = [1, 2, 3, 4, 5, 6];
var test1 = _.find(list,function (data,index,list){
console.log( data +','+ index );
return data % 3 == 0;
});
/*
1,0
2,1
3,2
*/
console.log(test1);// 3
var list2 = {
one:'a',
two:'b',
three:'c',
};
var test2 = _.find(list2,function (data,key,list){
console.log( data +','+ key );
return key == 'two';
});
/*
a,one
b,two
*/
console.log(test2);// b
5. _.filter
.filter(list, predicate, [context])(.select) 遍历list中的每个值,返回包含所有通过predicate迭代函数真值检测的元素值。(愚人码头注:如果存在原生filter方法,则用原生的filter方法。)
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
1、 运行cb()函数,并得到迭代函数predicate()的值:
predicate=optimizeCb(predicate,context);
=function(value, index, collection) {
return predicate.call(context, value, index, collection);
};
2、 运行_.each()函数,遍历obj对象,并判断是否满足迭代函数predicate的条件,满足,则把相应的值推进数组results中,不满足,则继续遍历,直到遍历完成为止,并返回results数组。
例:
var list = [1, 2, 3, 4, 5, 6];
var test1 = _.filter(list,function (data,index,list){
console.log( data +','+ index );
return data % 2 == 0;
});
/*
1,0
2,1
3,2
4,3
5,4
6,5
*/
console.log(test1);// [2, 4, 6]
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d',
five:'e',
six:'f',
seven:'g',
};
var test2 = _.filter(list2,function (data,key,list){
return key == 'one' || key=='five' || key=='seven';
});
console.log(test2);// ["a", "e", "g"]
6. _.reject
_.reject(list, predicate, [context]) 返回list中没有通过predicate真值检测的元素集合,与filter相反
_.reject = function(obj, predicate, context) {
// negate_.negate(predicate) (对立面;反面)
// 返回一个新的predicate函数的否定版本。
return _.filter(obj, _.negate(cb(predicate)), context);
};
1、 运行cb()函数,并得到迭代函数predicate()的值:
predicate=optimizeCb(predicate,context);
=function(value, index, collection) {
return predicate.call(context, value, index, collection);
};
2、 运行_.negate()函数,通过!非运算符,返回一个新的迭代函数predicate()的否定版本。
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
例:
var list = [1, 2, 3, 4, 5, 6];
var test1 = _.reject(list,function (data,index,list){
console.log( data +','+ index );
return data % 2 == 0;
});
/*
1,0
2,1
3,2
4,3
5,4
6,5
*/
console.log(test1);// [1, 3, 5]
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d',
five:'e',
six:'f',
seven:'g',
};
var test2 = _.reject(list2,function (data,key,list){
return key == 'one' || key=='five' || key=='seven';
});
console.log(test2);// ["b", "c", "d", "f"]
7. _.every
.every(list, [predicate], [context])(.all) 如果list中的所有元素都通过predicate的真值检测就返回true。
_.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;
if (!predicate(obj[currentKey], currentKey, obj)) return false;
}
return true;
};
1、 运行cb()函数,并得到迭代函数predicate()的值:
predicate=optimizeCb(predicate,context);
=function(value, index, collection) {
return predicate.call(context, value, index, collection);
};
2、 判断obj不是类数组对象,检索object拥有的所有可枚举属性的名称,并返回数组keys。
3、 使用for循环,判断迭代函数 predicate()的条件,当有一条数据不满足条件时,返回false,当所有数据满足条件时,返回true。
例:
var list = [2, 4, 6, 8, 10, 12];
var test1 = _.every(list,function (data,index,list){
return data % 2 == 0;
});
console.log(test1);// true
var list2 = {
one:'a',
two:'b',
three:'a',
four:'a',
};
var test2 = _.every(list2,function (data,key,list){
console.log(data);
return data == 'a';
});
/*
a
b
*/
console.log(test2);// false
8. _.some
.some(list, [predicate], [context])(.any) 如果list中有任何一个元素通过 predicate 的真值检测就返回true。一旦找到了符合条件的元素, 就直接中断对list的遍历。
_.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;
};
1、 运行cb()函数,并得到迭代函数predicate()的值:
predicate=optimizeCb(predicate,context);
=function(value, index, collection) {
return predicate.call(context, value, index, collection);
};
2、 判断obj不是类数组对象,检索object拥有的所有可枚举属性的名称,并返回数组keys。
3、 使用for循环,判断迭代函数 predicate()的条件,当有一条数据满足条件时,返回true,当所有数据不满足条件时,返回false。与_.every()函数相反。
例:
var list = [1, 2, 3, 4, 5, 6];
var test1 = _.some(list,function (data,index,list){
console.log(data);
return data % 2 == 0;
});
/*
1
2
*/
console.log(test1);// true
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d',
};
var test2 = _.some(list2,function (data,key,list){
console.log(data);
return data == 'b';
});
/*
a
b
*/
console.log(test2);// true
9. _.contains
.contains(list, value, [fromIndex])(.includes/_.include) 如果list包含指定的value则返回true(愚人码头注:使用===检测)。如果list 是数组,内部使用indexOf判断。使用fromIndex来给定开始检索的索引位置。
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
// _.values(object)
// 返回object对象所有的属性值。
if (!isArrayLike(obj)) obj = _.values(obj);
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
// _.indexOf(array, value, [isSorted])
// 返回value在该 array 中的索引值,如果value不存在 array中就返回-1。
// 使用原生的indexOf 函数,除非它失效。
// 如果您正在使用一个大数组,你知道数组已经排序,
// 传递true给isSorted将更快的用二进制搜索..,或者,
// 传递一个数字作为第三个参数,为了在给定的索引的数组中寻找第一个匹配值。
return _.indexOf(obj, item, fromIndex) >= 0;
};
1、 判断obj若为对象,则运行_.values(obj)函数,返回一个数组,其数组为所有obj属性的值。
2、 确保fromIndex的值为数字。
3、 运行_.indexOf()函数,并检测返回值是否为-1,若是:返回false,若否:返回true。
例:
var list = [2, 4, 6, 8, 10, 12];
var test1 = _.contains(list,6);
console.log(test1);// true
var test3 = _.contains(list,6,3);
console.log(test3);// false
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d',
};
var test2 = _.contains(list2,'c');
console.log(test2);// true
10. _.invoke
_.invoke(list, methodName, *arguments) 在list的每个元素上执行methodName方法。任何传递给invoke的额外参数,invoke都会在调用methodName方法的时候传递给它。
_.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);
});
});
var restArgs = function(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0);
var rest = Array(length);
for (var index = 0; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
};
1、 运行函数restArgs()函数,并运行里面的function()函数
2、 遍历obj对象,判断method是否为函数对象,若是,直接把函数赋给func变量,若否,得到每一个遍历obj的值value,并读取value对象中的method属性的值,并赋给func变量。
3、 判断func是否为null,若存在,运行func函数并存在参数数组[args],且作用域是value,若不存在,运行func函数。
例:
var list = [[5, 1, 7], [3, 2, 1]];
var test1 = _.invoke(list,'sort');
console.log(test1);// [[1,5,7],[1,2,3]]
11. _.pluck
_.pluck (list, propertyName) pluck也许是map最常使用的用例模型的简化版本,即萃取数组对象中某属性值,返回一个数组。
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
1、 运行 _.property函数,返回一个函数,这个函数返回任何传入的对象的key属性值obj(key): _.property= property (key);
var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
_.property = property;
即:
_.pluck = _.map(obj,function(key){
return function(obj){
return obj == null ? void 0 : obj[key];
}
}
_.pluck = _.map(obj,function(objChild ,key){
return objChild == null ? void 0 : objChild [key];
}
2、 运行_.map()函数,并得到返回值为obj属性值数组。
例:
var list = [
{name: 'moe', age: 40},
{name: 'larry', age: 50},
{name: 'curly', age: 60}
];
var test1 = _.pluck(list,'name');
console.log(test1);// ["moe", "larry", "curly"]
12. _.where
_.where (list, properties) 遍历list中的每一个值,返回一个数组,这个数组包含properties所列出的属性的所有的 键 - 值对。
_.where = function(obj, attrs) {
return _.filter(obj, _.matcher(attrs));
};
1、 运行_.matcher()函数,返回一个函数(断言函数),这个函数会给你一个断言可以用来辨别给定的对象是否匹配attrs指定键/值属性。
_.matcher = _.matches = function(attrs) {
// _.extendOwn(destination, *sources) Alias: assign
// 类似于 extend, 但复制source对象中自己的属性覆盖到destination目标对象。
// 并且返回 destination 对象. 复制是按顺序的,
// 所以后面的对象属性会把前面的对象属性覆盖掉(如果有重复).
// (愚人码头注:不包括继承过来的属性)
attrs = _.extendOwn({}, attrs);
return function(obj) {
// _.isMatch(object, properties)
// 告诉你properties中的键和值是否包含在object中。
return _.isMatch(obj, attrs);
};
};
即:
_.where = _.filter(obj,function(attrs){
attrs = _.extendOwn({}, attrs);
return function(obj){
return _.isMatch(obj,attrs);
}
});
2、 运行_.filter()函数,遍历obj中的每个值,返回包含所有通过function(attrs)迭代函数真值检测的元素值。
例:
var listOfPlays = [
{title: 'Cymbeline', author: 'Shakespeare', year: 1611},
{title: 'The Tempest',author: 'Shakespeare', },
{title: 'The Tempest', author: 'Shakespeare', year: 1611},
];
var obj = {author: "Shakespeare", year: 1611};
var list = _.where(listOfPlays,obj);
console.log(list);
/*[Object]
0: Object
author: "Shakespeare"
title: "Cymbeline"
year: 1611
__proto__: Object
1: Object
author: "Shakespeare"
title: "The Tempest"
year: 1611
__proto__: Object
length: 2
__proto__: Array[0]
*/
13. _.findWhere
_.findWhere (list, properties) 遍历整个list,返回匹配 properties参数所列出的所有 键 - 值 对的第一个值。如果没有找到匹配的属性,或者list是空的,那么将返回undefined。
_.findWhere = function(obj, attrs) {
return _.find(obj, _.matcher(attrs));
};
1、 运行_.matcher()函数,返回一个函数(断言函数),这个函数会给你一个断言可以用来辨别给定的对象是否匹配attrs指定键/值属性.
即:
_.findWhere = _.find(obj,function(attrs){
attrs = _.extendOwn({}, attrs);
return function(obj){
return _.isMatch(obj,attrs);
}
});
2、 运行_.find()函数,在obj中逐项查找,返回第一个通过function(attrs)迭代函数真值检测的元素值,如果没有值传递给测试迭代器将返回undefined,如果找到匹配的元素,函数将立即返回,不会遍历整个obj。
例:
var publicServicePulitzers = [
{year: 1348, reason: "For its public service in publishing."},
{year: 1918,newsroom: "The New York Times",reason: "For its public"},
{year: 1918, reason: "For its public service in publishing."},
{year: 2008, newsroom: "The New York Times",reason: "For its public"},
];
var obj = {newsroom: "The New York Times"};
var list = _.findWhere(publicServicePulitzers,obj);
console.log(list);
/*
Object {
year: 1918,
newsroom: "The New York Times",
reason: "For its public"
}
*/
14. _.max
_.max(list, [iteratee], [context]) 返回list中的最大值。如果传递iteratee参数,iteratee将作为list中每个值的排序依据。如果list为空,将返回-Infinity,所以你可能需要事先用isEmpty检查 list 。
_.max = function(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
value, computed;
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
//_.values(object)
//返回object对象所有的属性值。
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value > result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
result = v;
lastComputed = computed;
}
});
}
return result;
};
1、 判断迭代器iteratee是否为null或判断迭代器iteratee的类型为”number”、obj[0]的类型不为“object”,obj不为null。
若是:
a) 首先判断obj是否为类数组对象,是,则直接吧obj赋值给obj,否,则运行_.values(obj)函数,返回obj对象所有的属性值,并返回一个数组,赋值给obj。
b) 通过for循环,遍历新对象obj的每一个值,并赋值给value,判断value不为null,且value > result则把value赋值给result,直到找到最大值为止。并返回result值。
例:
var list2 = [3,45,23,67,43,21];
var test2 = _.max(list2);
console.log(test2);//67
var list3 = {
age1:'34',
age2:'42',
age3:'56',
age4:'43',
age5:'35'
};
var test3 = _.max(list3);
console.log(test3);//56
2、 若否:
a) 运行cb()函数,并得到迭代函数iteratee()的值:
iteratee=optimizeCb(iteratee,context);
=function(value, index, collection) {
return iteratee.call(context, value, index, collection);
};
b) 运行迭代函数iteratee,并把返回值赋值给computed。
c) 运行_.each()函数,遍历obj。判断computed > lastComputed 或computed完全等于负无穷,result完全等于负无穷,则把v的值赋给result,把computed的值赋给lastComputed。直到找到computed的最大值为止,并返回result值。
例:
var list = [
{name: 'moe', age: 40},
{name: 'larry', age: 70},
{name: 'curly', age: 60}
];
var test = _.max(list,function(value){
return value.age;
});
console.log(test);//Object{name: 'larry', age: 70}
15. _.min
_.min(list, [iteratee], [context]) 返回list中的最小值。如果传递iteratee参数,iteratee将作为list中每个值的排序依据。如果list为空,将返回-Infinity,所以你可能需要事先用isEmpty检查 list 。
_.min = function(obj, iteratee, context) {
var result = Infinity, lastComputed = Infinity,
value, computed;
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value < result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed < lastComputed || computed === Infinity && result === Infinity) {
result = v;
lastComputed = computed;
}
});
}
return result;
};
1、 判断迭代器iteratee是否为null或判断迭代器iteratee的类型为”number”、obj[0]的类型不为“object”,obj不为null。
若是:
a) 首先判断obj是否为类数组对象,是,则直接吧obj赋值给obj,否,则运行_.values(obj)函数,返回obj对象所有的属性值,并返回一个数组,赋值给obj。
b) 通过for循环,遍历新对象obj的每一个值,并赋值给value,判断value不为null,且value < result则把value赋值给result,直到找到最小值为止。并返回result值。
例:
var list2 = [3,45,23,67,43,21];
var test2 = _.min(list2);
console.log(test2);//3
var list3 = {
age1:'34',
age2:'42',
age3:'56',
age4:'43',
age5:'35'
};
var test3 = _.min(list3);
console.log(test3);//34
2、 若否:
a) 运行cb()函数,并得到迭代函数iteratee()的值:
iteratee=optimizeCb(iteratee,context);
=function(value, index, collection) {
return iteratee.call(context, value, index, collection);
};
b) 运行迭代函数iteratee,并把返回值赋值给computed。
c) 运行_.each()函数,遍历obj。判断computed < lastComputed 或computed完全等于正无穷,result完全等于正无穷,则把v的值赋给result,把computed的值赋给lastComputed。直到找到computed的最小值为止,并返回result值。
例:
var list = [
{name: 'moe', age: 40},
{name: 'larry', age: 70},
{name: 'curly', age: 60}
];
var test = _.min(list,function(value){
return value.age;
});
console.log(test);//Object{name: 'moe', age: 40}
16. _.sample
_.sample(list, [n]) 从 list中产生一个随机样本。传递一个数字表示从list中返回n个随机元素。否则将返回一个单一的随机项。
_.sample = function(obj, n, guard) {
// _.random(min, max)
// 返回一个min 和 max之间的随机整数。
// 如果你只传递一个参数,那么将返回0和这个参数之间的整数.
if (n == null || guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
// _.clone(object)
// 创建 一个浅复制(浅拷贝)的克隆object。
// 任何嵌套的对象或数组都通过引用拷贝,不是复制。
var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
var length = getLength(sample);
n = Math.max(Math.min(n, length), 0);
var last = length - 1;
for (var index = 0; index < n; index++) {
var rand = _.random(index, last);
var temp = sample[index];
sample[index] = sample[rand];
sample[rand] = temp;
}
return sample.slice(0, n);
};
1、 判断n是为null或guard存在。
a) 则判断obj若不为类数组对象,运行_.values(obj)函数,返回obj对象所有的属性值,并返回一个数组,赋值给obj。 b) 运行 _.random(obj.length - 1)函数,返回0与obj.length - 1之间的一个随机整数。从而,返回一个obj随机数。
2、 n存在
a) 判断obj是否为数组,若是,则运行_.clone(obj),拷贝一份新的数组赋值给sample,若否,则运行 _.values(obj)函数,返回obj对象所有的属性值,并返回一个数组,赋值给sample。
b) 读取sample的长度,并赋值给length。
c) 运行Math.min()函数,得出length与n中的最小值,并运行Math.max()函数,得到其最小值与0之间的最大值。
d) 得出sample最后一个索引值,赋值给last。
e) 运行for循环函数,遍历n次,运行 _.random(index,last)函数,返回index与last之间的一个随机整数,赋值给rand,相当于一个随机索引值。添加中间变量temp存储sample[index]的值。最后得到sample[index]和sample[rand]的值,并相互交换他们所在的位置,重新得到一个随机数组。
f) 返回新数组中0至n的值。
例:
var list = [1, 2, 3, 4, 5, 6];
var test = _.sample(list);
console.log(test);//6
var list = [1, 2, 3, 4, 5, 6];
var test2 = _.sample(list,3);
console.log(test2);//[6, 5, 4]
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d',
five:'e'
};
var test2 = _.sample(list2);
console.log(test2);//c
17. _.shuffle
_.shuffle(list) 返回一个随机乱序的 list 副本, 使用 Fisher-Yates shuffle 来进行随机乱序。
_.shuffle = function(obj) {
return _.sample(obj, Infinity);
};
1、 运行次函数_.shuffle()主要与运行 _.sample()函数同理,参数n为Infinity无穷大。故运行后将到一个随机的obj对象。
例:
var list = [1, 2, 3, 4, 5, 6];
var test = _.shuffle(list);
console.log(test);//[1, 5, 4, 2, 6, 3]
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d',
five:'e'
};
var test2 = _.shuffle(list2);
console.log(test2);//["c", "b", "e", "d", "a"]
18. _.sortBy
_.sortBy(list, iteratee, [context]) 返回一个排序后的list拷贝副本。如果传递iteratee参数,iteratee将作为list中每个值的排序依据。迭代器也可以是字符串的属性的名称进行排序的(比如 length)。
_.sortBy = function(obj, iteratee, context) {
var index = 0;
iteratee = cb(iteratee, context);
// _.pluck(list, propertyName)
// pluck也许是map最常使用的用例模型的简化版本,
// 即萃取数组对象中某属性值,返回一个数组。
return _.pluck(_.map(obj, function(value, key, list) {
return {
value: value,
index: index++,
criteria: iteratee(value, key, list)
};
}).sort(function(left, right) {
// arrayobj.sort(sortfunction)
// 返回一个元素已经进行了排序的 Array 对象。
// arrayObj 必选项。任意 Array 对象。
// sortFunction 可选项。是用来确定元素顺序的函数的名称。
// 如果这个参数被省略,那么元素将按照 ASCII 字符顺序进行升序排列。
// 如果为 sortfunction 参数提供了一个函数,那么该函数必须返回下列值之一:
// 负值,如果所传递的第一个参数比第二个参数小。
// 零,如果两个参数相等。
// 正值,如果第一个参数比第二个参数大。
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;
}), 'value');
};
1、 运行cb()函数,
a) 若iteratee为迭代函数则:
iteratee=optimizeCb(iteratee,context);
=function(value, index, collection) {
return iteratee.call(context, value, index, collection);
};
b) 若iteratee为字符串或null,则
iteratee=_.property(iteratee);
=function(iteratee) {
return function(obj) {
return obj == null ? void 0 : obj[iteratee];
};
};
2、 运行_.map()函数,返回一个新的数组,并运行sort()函数,对此数组按照一定规则进行排序。得到新的数组。
3、 运行_.pluck()函数,返回最终得到的数组。
例:
var list = [
{name: 'moe', age: 40},
{name: 'larry', age: 70},
{name: 'curly', age: 60}
];
var test = _.sortBy(list,'name');
console.log(test);
/*
[
{name: 'curly', age: 60},
{name: 'larry', age: 70},
{name: 'moe', age: 40}
];
*/
var test4 = _.sortBy(list,'age');
console.log(test4);
/*
[
{name: 'moe', age: 40},
{name: 'curly', age: 60},
{name: 'larry', age: 70}
];
*/
var list2 = [3,45,23,67,43,21];
var test2 = _.sortBy(list2,function(num){
//Math.sin(x)
//x 的正玄值。返回值在 -1.0 到 1.0 之间;
return Math.sin(num);
});
console.log(test2);//[67, 23, 43, 3, 21, 45]
var list3 = {
age1:'34',
age2:'42',
age3:'56',
age4:'43',
age5:'35'
};
var test3 = _.sortBy(list3);
console.log(test3);//["34", "35", "42", "43", "56"]
19. _.groupBy
_.groupBy(list, iteratee, [context]) 把一个集合分组为多个集合,通过 iterator 返回的结果进行分组。如果 iterator 是一个字符串而不是函数, 那么将使用 iterator 作为各元素的属性名来对比进行分组。
_.groupBy = group(function(result, value, key) {
if (_.has(result, key)) result[key].push(value);
else result[key] = [value];
});
1、 运行group()函数
var group = function(behavior, partition) {
return function(obj, iteratee, context) {
var result = partition ? [[], []] : {};
iteratee = cb(iteratee, context);
_.each(obj, function(value, index) {
var key = iteratee(value, index, obj);
behavior(result, value, key);
});
return result;
};
};
2、 得到behavior的值为
behavior=function(result, value, key) {
if (_.has(result, key)) result[key].push(value);
else result[key] = [value];
}
3、 判断partition是否存在,若存在,则result=[[], []],若不存在,则result={}。
4、 运行cb()函数,
a) 若iteratee为迭代函数则:
iteratee=optimizeCb(iteratee,context);
=function(value, index, collection) {
return iteratee.call(context, value, index, collection);
};
b) 若iteratee为字符串或null,则
iteratee=_.property(iteratee);
=function(iteratee) {
return function(obj) {
return obj == null ? void 0 : obj[iteratee];
};
};
5、 运行_.each()函数,遍历obj对象。得到key的值。
6、 运行behavior()函数
a) 运行_.has(result, key)函数,判断对象result是否包含给定的键key。若result存在key,则把当前value值push到result[key]中;
b) 若result不存在key,则直接把value存到数组中并赋给result[key];
7、 返回result的最终结果。
例:
var list = [
{name: 'moe', age: 40},
{name: 'larry', age: 40},
{name: 'curly', age: 60}
];
var test = _.groupBy(list,'age');
console.log(test);
/*
{
40:[
{name: 'moe', age: 40},
{name: 'larry', age: 40}
],
60:[
{name: 'curly', age: 60}
]
};
*/
var list2 = [1.3, 2.1, 2.4];
var test2 = _.groupBy(list2,function(num){
//Math.floor(x)
//返回小于等于x的最大整数
return Math.floor(num);
});
console.log(test2);
/*
{
1:[1.3],
2:[2.1,2.4]
}
*/
20. _.indexBy
_.indexBy(list, iteratee, [context])
给定一个list,和 一个用来返回一个在列表中的每个元素键 的iterator 函数(或属性名),返回一个每一项索引的对象。和groupBy非常像,但是当你知道你的键是唯一的时候可以使用indexBy 。
_.indexBy = group(function(result, value, key) {
result[key] = value;
});
1、 与_.groupBy()函数一样,先运行group()函数
2、 得到behavior的值为
behavior=function(result, value, key) {
result[key] = value;
}
3、 直接返回result的最终结果。
4、 此处与_.groupBy()函数最大的区别是, _.indexBy()函数中迭代函数iteratee得到的键值是唯一的,返回的result[key]结果为一个对象。而 _.groupBy()函数中迭代函数iteratee得到的键值不是唯一的,返回的result[key]结果为一个数组。
例:
var list = [
{name: 'moe', age: 40},
{name: 'larry', age: 70},
{name: 'curly', age: 60}
];
var test = _.indexBy(list,'age');
console.log(test);
/*
{
40:{name: 'moe', age: 40},
60:{name: 'curly', age: 60},
70:{name: 'larry', age: 70}
};
*/
var list2 = [1.3, 2.1, 3.4];
var test2 = _.indexBy(list2,function(num){
//Math.floor(x)
//返回小于等于x的最大整数
return Math.floor(num);
});
console.log(test2);
/*
{
1:1.3,
2:2.1,
3:3.4
}
*/
21. _.countBy
_.countBy(list, iteratee, [context]) 排序一个列表组成一个组,并且返回各组中的对象的数量的计数。类似groupBy,但是不是返回列表的值,而是返回在该组中值的数目。
_.countBy = group(function(result, value, key) {
if (_.has(result, key)) result[key]++;
else result[key] = 1;
});
1、 与_.groupBy()函数一样,先运行group()函数
2、 得到behavior的值为
behavior=function(result, value, key) {
if (_.has(result, key)) result[key]++;
else result[key] = 1;
}
3、 运行behavior()函数
a) 运行 _.has(result, key)函数,判断对象result是否包含给定的键key。若result存在key,则result[key]++;
b) 若result不存在key,则result[key]=1;
4、 返回result的最终结果。
5、 此处与_.groupBy()函数最大的区别是, _.countBy()函数的结果是返回在该组中值的数目,而 _.groupBy()函数的结果是返回列表的值。
例:
var list = [
{name: 'moe', age: 40},
{name: 'larry', age: 40},
{name: 'curly', age: 60}
];
var test = _.countBy(list,'age');
console.log(test);//{40: 2, 60: 1};
var list2 = [1.3, 2.1, 2.4];
var test2 = _.countBy(list2,function(num){
//Math.floor(x)
//返回小于等于x的最大整数
return Math.floor(num);
});
console.log(test2);//{1: 1, 2: 2}
var list3 = [1, 2, 3,4,5];
var test3 = _.countBy(list3, function(num) {
return num % 2 == 0 ? 'even': 'odd';
});
console.log(test3);//{odd: 3, even: 2}
22. _.toArray
_.toArray(list) 把list(任何可以迭代的对象)转换成一个数组,在转换 arguments 对象时非常有用。
// 这个正则按“|”分割,包含三个部分
// [^\ud800-\udfff] 是普通的 BMP 字符,表示不包含代理对代码点的所有字符
// [\ud800-\udbff][\udc00-\udfff] 是成对的代理项对,表示合法的代理对的所有字符
// [\ud800-\udfff] 是未成对的代理项字,表示代理对的代码点(本身不是合法的Unicode字符)
var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (_.isString(obj)) {
// Keep surrogate pair characters together
// match() 方法可在字符串内检索指定的值,
// 或找到一个或多个正则表达式的匹配。
return obj.match(reStrSymbol);
}
if (isArrayLike(obj)) return _.map(obj, _.identity);
return _.values(obj);
};
1、 判断obj不存在为null时,直接返回空数组[]。
2、 判断obj为数组时,直接返回obj。
例:
var test = (function(){
return _.toArray(arguments).slice(1);
})(1, 2, 3, 4);
console.log(test);//[2, 3, 4]
var list2 = [1,2,3,4,5,6];
var test2 = _.toArray(list2);
console.log(test2);//[1, 2, 3, 4, 5, 6]
3、 判断obj为字符串时,obj运行正则表达式obj.match(reStrSymbol),并返回个字符串字母组成的数组。
var test4 = _.toArray('hello world!');
console.log(test4);
//["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
4、 判断obj为类数组对象时,运行_.map(obj)函数,并返回数组。
var list5 = {
0:'a',
1:'b',
2:'c',
3:'d',
length:4
};
var test5 = _.toArray(list5);
console.log(test5);//["a", "b", "c", "d"]
5、 判断obj为对象时,运行_.values(obj)函数,返回obj对象所有的属性值。
var list3 = {
one:'a',
two:'b',
three:'c',
four:'d'
};
var test3 = _.toArray(list3);
23. _.size
_.size(list) 返回list的长度。
_.size = function(obj) {
if (obj == null) return 0;
return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};
1、 判断obj为null,则返回0。
2、 判断obj为类数组对象,返回对象的长度。
例:
var list = [1,2,3,4,5,6];
var test = _.size(list);
console.log(test);//6
3、 判断obj为对象,运行_.keys(obj)函数,得到obj对象的属性名称,并返回此数组的长度。
例:
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d'
};
var test2 = _.size(list2);
console.log(test2);//4
24. _.partition
_.partition(array, predicate) 拆分一个数组(array)为两个数组:第一个数组其元素都满足predicate迭代函数,而第二个的所有元素均不能满足predicate迭代函数。
_.partition = group(function(result, value, pass) {
result[pass ? 0 : 1].push(value);
}, true);
1、 运行group()函数,得到behavior的值为
behavior=function(result, value, pass) {
result[pass ? 0 : 1].push(value);
}
2、 由于partition的值为true,故result=[[], []]。
3、 运行behavior()函数,若满足pass条件,则把obj的值value传入数组索引为0的位置,若不满足pass条件,则把obj的值value传入数组索引为1的位置。
例:
var list = [1,2,3,4,5,6];
var test = _.partition(list,function (value){
return value % 2 == 0;
});
console.log(test);//[[2, 4, 6],[1, 3, 5]]
var list2 = {
one:'a',
two:'b',
three:'c',
four:'d'
};
var test2 = _.partition(list2,function (value){
return value == 'a' || value == 'c';
});
console.log(test2);//[['a', 'c'],['b', 'd']]