在JavaScript中处理长列表时,分块(chunking)是一种非常常见的数据预处理手段。它可以把一个包含大量元素的一维数组按固定大小切成若干个子数组,方便分页渲染、批量接口提交或并发控制。与分块紧密相关的另一个需求是把处理结果转换为JSON数组,用于网络传输、本地存储或跨语言数据交换。虽然这两件事分开来看都不复杂,但要在数据量较大时保持流程顺畅,实现细节会直接影响性能和内存占用。

列表分块的基础思路与边界处理
分块的核心逻辑并不难:维护一个起始索引,每次向后移动固定步长,用 slice 截取一段放入结果数组,直到索引超过原数组长度。下面是最直接的实现:
function chunkBySlice(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
这段代码不会修改原数组,因为 slice 总是返回新数组。即使最后一次截取的长度不足 size,例如数组长度是5、块大小是2,最后一次也只会得到 [5],不会产生越界错误。因此 slice 方案在边界处理上非常安全,代码也很容易理解。
不过,使用时仍需校验 size。如果传入0或负数,循环步长会变成0或负数,导致无限循环或异常结果。稳妥的做法是对块大小取整并限制最小值为1,例如:
function chunkBySlice(arr, size) {
size = Math.max(1, Math.floor(size));
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
由于上面示例中 pre 的 class 属性写错了,这里纠正一下。实际开发时,务必保证 class="brush:javascript;toolbar:false" 的引号完整闭合。分块后的结果是一个二维数组,每个子数组就是一批数据。后续如果要转换为JSON,本质上是把这个二维数组交给 JSON.stringify 处理。
三种分块实现方式的性能对比
除了 slice 循环,另一种常见做法是用 splice。它会在原数组上删除并返回指定区间,因此需要先拷贝一份原数组,避免破坏外部数据:
function chunkBySplice(arr, size) {
const result = [];
const copy = [...arr];
while (copy.length > 0) {
result.push(copy.splice(0, size));
}
return result;
}
这种写法读起来比较直观,每次从副本头部切掉一块,直到切完为止。但 splice 在内部需要移动后续所有元素的位置,时间复杂度较高。当数组长度达到数万甚至数十万时,频繁调用 splice 会明显拖慢执行速度。因此 splice 方案更适合数据量较小、对代码简洁性要求高的场景。
第三种方案是使用 Array.from 生成一个指定长度的数组,然后在回调里根据索引计算切片位置:
const chunkByArrayFrom = (arr, size) =>
Array.from(
{ length: Math.ceil(arr.length / size) },
(_, i) => arr.slice(i * size, i * size + size)
);
它的优点是代码短小、函数式风格明确,适合中小型数组或工具函数库。但内部仍然依赖 slice,并且 Array.from 会创建中间数组,在超大数组下内存分配次数更多。综合来看,如果追求稳定和高性能,建议优先使用基于 for 循环加 slice 的实现。
在实际测试中,对10万条数字进行分块,块大小为1000时,slice 循环通常能在几毫秒内完成,而 splice 会慢一个数量级。虽然现代JavaScript引擎优化很好,但这种差距在移动端或低性能设备上仍可能影响用户体验。因此选择实现方式时,不应只看代码长短,还要考虑数据规模和运行环境。
分块结果转JSON数组的细节与避坑
分块得到二维数组后,通常需要序列化为JSON字符串,以便发送给后端或保存到本地。最简单的方式就是直接调用 JSON.stringify:
const data = [1, 2, 3, 4, 5]; const chunked = chunkBySlice(data, 2); const json = JSON.stringify(chunked); console.log(json); // [[1,2],[3,4],[5]]
转换后的JSON字符串仍然是二维数组结构,接收方用 JSON.parse 可以还原成新的JavaScript数组。但需要注意的是,JSON.stringify 对某些特殊值的处理并不总是符合直觉。如果原数组包含 undefined、函数或 Symbol,在数组序列化时这些值会被转换成 null:
const special = [undefined, function () {}, Symbol('x'), 1];
console.log(JSON.stringify(special)); // [null,null,null,1]
如果不希望这些值悄悄变成 null,应该在分块前先对原始数据进行清洗。比如用 map 把函数调用结果取出,或者用 filter 移除无效项。另一个常见错误是对象中存在循环引用,例如:
const obj = { name: 'test' };
obj.self = obj;
console.log(JSON.stringify(obj)); // 抛出 TypeError: Converting circular structure to JSON
遇到这种情况,可以传入 JSON.stringify 的第二个参数 replacer 来过滤循环引用字段,或者使用第三方序列化库。对于分块来说,如果块内的对象相互引用,序列化前需要先打破引用关系,例如只保留必要的ID或扁平化结构。
另外,如果数组中包含 BigInt,直接序列化会抛出异常,因为JSON标准不支持BigInt。解决办法是在分块前把 BigInt 转成字符串或数字。理解了这些限制之后,就能写出更健壮的分块转JSON工具。
面向大数据量的工程优化建议
当数据规模很大时,除了选择 slice 循环,还可以通过预分配结果数组来减少运行时扩容带来的开销。虽然JavaScript数组本身是动态的,但提前创建长度为 Math.ceil(arr.length / size) 的数组能让内存分配更集中:
function chunkWithPrealloc(arr, size) {
size = Math.max(1, Math.floor(size));
const total = Math.ceil(arr.length / size);
const result = new Array(total);
for (let i = 0; i < total; i++) {
result[i] = arr.slice(i * size, i * size + size);
}
return result;
}
这种写法避免了 push 过程中可能发生的多次扩容,对超大数组有一定改善。不过现代引擎对 push 的优化已经很到位,预分配的收益在多数场景下并不明显。真正重要的是避免在循环内部创建不必要的临时数组或使用高开销的方法。
另一个优化方向是使用生成器实现惰性分块。如果分块后的数据不需要一次性全部保存在内存中,比如逐批发送请求,可以用 function* 按需生成:
function* chunkGenerator(arr, size) {
size = Math.max(1, Math.floor(size));
for (let i = 0; i < arr.length; i += size) {
yield arr.slice(i, i + size);
}
}
const data = [1, 2, 3, 4, 5];
const iterator = chunkGenerator(data, 2);
console.log(iterator.next().value); // [1, 2]
console.log(iterator.next().value); // [3, 4]
惰性分块不会一次性创建所有子数组,内存占用更稳定,特别适合流式处理或不确定是否要全部分块的场景。需要完整二维数组时,可以再用 Array.from(chunkGenerator(arr, size)) 收集结果。
最后给一个完整的工具函数,它同时处理类型校验、空数组和非法块大小,并返回可序列化的分块结果:
function chunkArray(arr, size = 1) {
if (!Array.isArray(arr)) {
throw new TypeError('Expected an array');
}
size = Math.max(1, Math.floor(size));
if (arr.length === 0) return [];
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
function chunkToJSON(arr, size) {
return JSON.stringify(chunkArray(arr, size));
}
这个函数可以直接用在分页接口、批量导出或前端任务队列中。分块大小建议根据单条数据体积调整:纯数字或短字符串可以取500到2000,包含大量字段的对象则取50到200。配合 JSON.stringify 时,如果数据中包含敏感字段,还可以通过 replacer 做字段裁剪,减小传输体积。
JavaScript数组分块JSON数组转换Array.from修改时间:2026-08-28 17:41:38