前言
lodash 中的 chunk 函数是一个比较实用的适用于数组工具函数,作用是将创建一个新数组,将传入的数组根据 size 参数的大小进行切片分组。如果最后数组无法整除的,就将剩余元素都放到最后一块
这个函数虽然原理比较简单,但是在 lodash 当中为了处理一些边界情况,还是很细致的对这个函数做了很多条件判断和处理,因此其健壮性很强,对应的可以对类似的类数组对象也做出处理
思路分析
源码分析
chunk
1. 传入参数:
array传入一个数组size传入一个数字,决定新数组的元素个数,即将数组分成多少份guard传入迭代器对象,原意图是想配合迭代方法使用
2. 源码分析
function chunk(array, size, guard) {
if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
size = 1;
} else {
size = nativeMax(toInteger(size), 0);
}
var length = array == null ? 0 : array.length;
if (!length || size < 1) {
return [];
}
var index = 0,
resIndex = 0,
result = Array(nativeCeil(length / size));
while (index < length) {
result[resIndex++] = baseSlice(array, index, (index += size));
}
return result;
}
这里设置 guard 、或是没设置 size 情况下默认为 size 为 1;若是 size 传入其它则一律转成整数类型
if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
size = 1;
} else {
size = nativeMax(toInteger(size), 0);
}
判断 array 是否为空,然后判断传入的对象 length 属性是否存在,或是 size 是否为负数,如果没经过这里直接返回空数组
var length = array == null ? 0 : array.length;
if (!length || size < 1) {
return [];
}
初始化,对分块数块等进行分配,通过 baseSlice 方法复制指定位置的元素到新数组,然后分配给 result。最终返回 result
var index = 0,
resIndex = 0,
result = Array(nativeCeil(length / size));
while (index < length) {
result[resIndex++] = baseSlice(array, index, (index += size));
}
return result;
baseSlice
1. 传入参数
array传入一个数组start传入复制起点end传入复制终点
2. 源码分析
function baseSlice(array, start, end) {
var index = -1,
length = array.length;
if (start < 0) {
start = -start > length ? 0 : (length + start);
}
end = end > length ? length : end;
if (end < 0) {
end += length;
}
length = start > end ? 0 : ((end - start) >>> 0);
start >>>= 0;
var result = Array(length);
while (++index < length) {
result[index] = array[index + start];
}
return result;
}
初始化,将 start 和 end 都转化为正数,注意如果 start 和 end 原来是负数的话,会从数组末尾数 start 和 end 数字
var index = -1,
length = array.length;
if (start < 0) {
start = -start > length ? 0 : (length + start);
}
end = end > length ? length : end;
if (end < 0) {
end += length;
}
对 start 无符号右移, end - start 结果的符号位将成为正数,并取整。因为数字存储的是二进制补码,所以相当于对负数结果加上 2^32 ,保证其为非负数(有意义时取正,无意义时缺省 0 )。之后 index 从 0 开始,将对应 start 位置的 array 的元素,赋值给 index 位置的 result 的元素
length = start > end ? 0 : ((end - start) >>> 0);
start >>>= 0;
// 创建新数组
var result = Array(length);
while (++index < length) {
result[index] = array[index + start];
}
return result;
}
类数组对象
因为 chunk 可以用于类数组对象的分组,因此这里也简单说下类数组对象
首先明确定义,有 length 属性,且其属性都是非负整数就可以称为类数组对象。
Chrome 浏览器中加上 splice 方法的对象展示就会从 {} 转化为 []
lodash 中还有相应的判断方法 isArrayLike ,其中就是判断了 length 属性,以后有机会也会分析一下
与数组不同,类数组对象的原型链,或者说其继承的祖先类型没有 Array,因此也不能使用 forEach、map、reduce 等方法,也因此要对其迭代的话就需要用到 for 循环或是 for ……in,for……of 因为需要对象本身需要可迭代,所以除了自定义了 Symbol.iterator 的情况外不可以使用
可以后面附加一个自定义的 Symbol.iterator
想要对类数组对象进行处理,除了上述的方法方法外,还可以将其直接转化为数组。Array.from 就是内置的处理这一情况的函数,传入参数之后会返回一个新的数组,数组元素都是按顺序排列的原对象属性
类数组对象比较多见于 DOM 选择的结果,比如 document.querySelectorAll 会返回一个 NodeList 的类数组对象
总结
chunk 方法作为一个数组方法,常用于对数组的切片分组,可以将数组进行升级维度,对数组进行分块
chunk 方法本身的健壮性也可以保证其可以用在类数组中
lodash 在底层实现时复用了 baseSlice 方法,因此 chunk 方法时间复杂度是 O(n^2)