前言
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)