每次小章节的开题都懊恼写什么好,所以直接接下文 (~o▔▽▔)~o o~(▔▽▔o~) 。
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
if (n == null || guard) return array[0];
return _.initial(array, array.length - n);
};
_.first
用于返回数组中从左到右指定数量 n 的结果集,传入 array、n、guard 三个参数中 array 只能为 Array,当 n = null
时返回数组第一个元素,这里须要解说的是 _.initial
函数是与 _.first
完整对峙的函数,它用于返回数组中从左到右指定数量 Array.length - n
的结果集。
_.initial = function(array, n, guard) {
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};
那末它是怎样完成的呢,依然是运用数组 Array 的 Array.prototype.slice.call(array, start, end);
完成,这个观点请参看:Array.prototype.slice()。
_.last = function(array, n, guard) {
if (array == null) return void 0;
if (n == null || guard) return array[array.length - 1];
return _.rest(array, Math.max(0, array.length - n));
};
_.last
是返回数组中从右到左指定数量 n 的结果集。完成道理照旧 Array.prototype.slice.call(array, start, end);
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, n == null || guard ? 1 : n);
};
_.rest
用于返回数组中从右到左指定数量 Array.length - n
的结果集。
_.compact = function(array) {
return _.filter(array, Boolean);
};
_.compact
,我喜好称它为过滤器,过滤坏的数据,那末什么样的数据为坏数据呢,我们能够看下 _.filter
,前面讲 _.filter
吸收三个参数 obj, predicate, context
,个中 predicate 照旧由 cb 处置惩罚,那末这里 _.compact
传的 predicate 是 Boolean = function Boolean() { [native code] }
,这是一个 JAVASCRIPT 内置的函数用于 Boolean 推断,我们能够参考 Boolean 和 Boolean data type。那末重点来了,什么的值会是 Boolean 函数断言为 false 呢,答案就是 false, 0, "", null, undefined, NaN
,这个可不是我瞎扯或许 copy 官网,我是有理论依据的(vˍv),铛铛当,看这里 Truthy。
var flatten = function(input, shallow, strict, output) {
output = output || [];
var idx = output.length;
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
flatten(value, shallow, strict, output);
idx = output.length;
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
};
flatten 传入四个参数,input, shallow, strict, output
,个中我们能够经由过程 flatten 内部的 for 循环中 length = getLength(input);
晓得 input 数据类型为 Array。然后经由过程对 shallow, strict
两个 Boolean 型变量的掌握实行响应的数据处置惩罚体式格局。比方 shallow 为 false 会一向实行 flatten(value, shallow, strict, output);
和 output[idx++] = value;
对多维数组举行一维数组的转换。
_.flatten = function(array, shallow) {
return flatten(array, shallow, false);
};
_.flatten
函数用于对多维度数组举行扁平化处置惩罚,行将恣意维数的数组转换为一维数组,上面已说到了这个的完成体式格局。
_.without = restArgs(function(array, otherArrays) {
return _.difference(array, otherArrays);
});
_.without
用于删除数组中的某些特定元素。它由 _.difference
组成。
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
if (!_.isBoolean(isSorted)) {
context = iteratee;
iteratee = isSorted;
isSorted = false;
}
if (iteratee != null) iteratee = cb(iteratee, context);
var result = [];
var seen = [];
for (var i = 0, length = getLength(array); i < length; i++) {
var value = array[i],
computed = iteratee ? iteratee(value, i, array) : value;
if (isSorted) {
if (!i || seen !== computed) result.push(value);
seen = computed;
} else if (iteratee) {
if (!_.contains(seen, computed)) {
seen.push(computed);
result.push(value);
}
} else if (!_.contains(result, value)) {
result.push(value);
}
}
return result;
};
_.uniq
是数组去重,完成道理是假如 isSorted 及背面元素省略,那末 _.uniq 简化为:
_.uniq = _.unique = function(array) { context = null; iteratee = null; isSorted = false; var result = []; var seen = []; for (var i = 0, length = getLength(array); i < length; i++) { var value = array[i]; if (!_.contains(result, value)) { result.push(value); } } return result; };
我们能够看到其中心代码只要 if (!_.contains(result, value))
,用于推断数组中是不是包括其值,以此到达数组去重的目标。是这里我想说的是 context、iteratee、isSorted 变成了未定义的参数,作者没有处置惩罚它会在这类状况下变成全局污染。
接下来我们说一下传入 array, isSorted, iteratee
三个参数的状况,我们已晓得 isSorted 默以为 false,代表去重,那末假如定义 isSorted 为 true 则就是不去重,假如 isSorted 是回调函数,则默许内部从新定义 isSorted 为 false,并将回调函数赋给 iteratee,然后很悲剧的 iteratee 参数依然是没有 var 过的,又污染了啊(‧_‧?) 。大抵就是这酱了。
_.union = restArgs(function(arrays) {
return _.uniq(flatten(arrays, true, true));
});
_.union
对多个一维数组举行并运算,实际上就是加强版的 _.uniq
。在代码中作者起首用 flatten 函数处置惩罚参数,之前我们说到 flatten 是用于多个多维数组举行一名转换,实际上就是要把 arrays 转换。这里有同砚能够问道 flatten 直吸收一个 Array 剩下的值是 Boolean 啊,那末运用 _.union
的时刻是一次性传入 n 个 Array(如如许:_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
),说不通啊。所以我要说的是 restArgs 这个函数,将传入参数转换为一个数组举行 func.apply(this, args)
到 restArgs 的回调函数 function(arrays) {}
中,以此到达 flatten 函数 arrays 接到的是一个一维数组的鸠合。末了经由过程 _.uniq
函数对数组举行处置惩罚。
_.intersection = function(array) {
var result = [];
var argsLength = arguments.length;
for (var i = 0, length = getLength(array); i < length; i++) {
var item = array[i];
if (_.contains(result, item)) continue;
var j;
for (j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
};
_.intersection
用于猎取多个一维数组的雷同数据的鸠合,即交集。又是一番对 Array 的 for 啊 for 啊 for,然后 if 然后 push,置信人人这么智慧,不必多说了,由于这个函数很直白,没太多可讲的。
_.difference = restArgs(function(array, rest) {
rest = flatten(rest, true, true);
return _.filter(array, function(value){
return !_.contains(rest, value);
});
});
_.difference
函数的完成与 _.union
相似,都是经由过程 restArgs 对 n 个传参举行数组改变,然后赋给回调函数,区分在于这个函数能够越发庞杂,它起首 restArgs 回调写了两个传参 array, rest
,但实际上 rest 是 undefined,以后在回调内部给 rest 赋值为 flatten 函数处置惩罚以后的数组,即扁平化后的一维数组。由于 restArgs 函数只要一个 function 回调,所以内部实行 return func.call(this, arguments[0], rest);
,返回的是第一个数组和其他数组的鸠合,即 array, rest
。
_.unzip = function(array) {
var length = array && _.max(array, getLength).length || 0;
var result = Array(length);
for (var index = 0; index < length; index++) {
result[index] = _.pluck(array, index);
}
return result;
};
_.unzip
用于将多个数组中元素根据数组下标举行拼接,只吸收一个二维数组,返回值同样是一个二维数组。
_.zip = restArgs(_.unzip);
_.zip
与 _.unzip
不同之处在于它能够传入不定的一维数组参数然后经由过程 restArgs 函数转换完成 _.unzip
传参的结果。
_.object = function(list, values) {
var result = {};
for (var i = 0, length = getLength(list); i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
_.object
用于将数组转换成对象。
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;
};
};
createPredicateIndexFinder 这个函数适用于天生 _.findIndex
之类的函数,当我们看到 return index;
的是后就已能够晓得,其中心是与数组下标有关。
_.findIndex = createPredicateIndexFinder(1);
_.findIndex
函数由 createPredicateIndexFinder 包装而成,我们能够看到它的默许传值是 1
,也就是:
_.findIndex = function(array, predicate, context) { predicate = cb(predicate, context); for (var index >= 0; index < getLength(array); index += 1) { if (predicate(array[index], index, array)) return index; } return -1; };
个中 predicate 是回调函数吸收 array[index], index, array
三个值用于 Boolean 推断,终究结果是返回相符划定规矩的数组中的第一条数据的数组下标。
_.findLastIndex = createPredicateIndexFinder(-1);
_.findLastIndex
望文生义就是返回数组中相符划定规矩的末了一条数据的下标,说直白了就是遍历数组的时刻从右往左罢了。
_.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
官网诠释说 运用二分查找肯定value在list中的位置序号,value按此序号插进去能坚持list原有的排序。
,很绕口,这里我们须要注重的是假如举行 _.sortedIndex
查找这个特定的序列号,肯定要事先将 array 举行按需排序。
var createIndexFinder = function(dir, predicateFind, sortedIndex) {
return function(array, item, idx) {
var i = 0, length = getLength(array);
if (typeof idx == 'number') {
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);
return array[idx] === item ? idx : -1;
}
if (item !== item) {
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) {
if (array[idx] === item) return idx;
}
return -1;
};
};
createIndexFinder,看定名就能够晓得照旧与数组下标有关。我们能够看到数据处置惩罚的一个关键是 idx,它多是一个数字也多是一个字符串或许对象。当它是 Number 的时刻遵照 idx 是限制查找局限的数组下标划定规矩,假如它是其他的则运用 sortedIndex 函数查找到 idx 的数组下标再岁数组查找局限举行限制。
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
_.indexOf
函数与 _.findIndex
区分在于 _.findIndex
须要查找的数据能够存在于数组中也能够不存在数组中,而 _.indexOf
的 predicateFind 肯定是数组中的元素。同时也用 array, item, idx
三个参数中的 idx 限制开始查找的局限。
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
_.lastIndexOf
查找数组中的相符结果的末了条数据的数组下标。
_.range = function(start, stop, step) {
if (stop == null) {
stop = start || 0;
start = 0;
}
if (!step) {
step = stop < start ? -1 : 1;
}
var length = Math.max(Math.ceil((stop - start) / step), 0);
var range = Array(length);
for (var idx = 0; idx < length; idx++, start += step) {
range[idx] = start;
}
return range;
};
_.range
用于天生一个有序的数组,经由过程 start 和 stop 限制数组局限,经由过程 step 限制差值。
_.chunk = function(array, count) {
if (count == null || count < 1) return [];
var result = [];
var i = 0, length = array.length;
while (i < length) {
result.push(slice.call(array, i, i += count));
}
return result;
};
_.chunk
,这个函数量前官网并没有释义,预计作者遗忘加进去了吧,我们看到 chunk 很天然的就应该想到 stream 的观点,这里也差不多,只不过拆分的不限制是 Buffer 数组, _.chunk
传入两个参数 Array 以及 count,个中 count 用来限制拆分出的每一组的大小,举个栗子:
_.chunk([1,2,3,4,5,6,7,8,9], 1) [[1],[2],[3],[4],[5],[6],[7],[8],[9]] _.chunk([1,2,3,4,5,6,7,8,9], 2) [[1,2],[3,4],[5,6],[7,8],[9]]
但是凡是对 stream 的观点有所相识都晓得这个函数吧,没什么特别的处所。