☰
JavaScript slice() 方法实战指南:从 Array 与 String 切片到 refine 源码中的分页、导出与路由参数解析
2026/9/25 2:44:02 网站建设 项目流程

JavaScript slice() 方法实战指南:从 Array 与 String 切片到 refine 源码中的分页、导出与路由参数解析

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

本篇技术指南系统讲解 JavaScript 中Array.prototype.slice与String.prototype.slice的方法签名、参数语义与边界行为,并结合 refine(一个用于构建内部工具、管理面板、仪表盘和 B2B 应用的 React 框架)仓库中的真实源码,展示slice()在客户端分页、数据导出、路由参数提取和字符串转换中的落地方式。读完本文,你可以准确掌握slice()的取/不取边界、负偏移、稀疏数组等全部细节,并能在类似 refine 的管理后台项目中将其应用于分页视图、不可变数据操作和字符串处理。

一、背景:slice 属于哪一类迭代方法

slice()是 JavaScript 迭代方法系列的一员。迭代(Iteration)指遍历一组数据并对每个元素执行操作(变换数据或产生副作用)的过程。常见的迭代方法有Array.prototype.forEach、Array.prototype.map和Array.prototype.reduce,它们会遍历集合中的全部元素;而slice()、some()、every()等方法只需部分遍历即可完成工作。

从迭代角度看,slice()与includes()类似,可能只遍历部分元素。refine 是面向 React 的内部工具框架,其文档站与示例代码中大量出现对数组和字符串的切片操作——例如在 blog 系列文档 中讲解迭代方法、在packages/下各包的源码中用它实现分页与截断。本文以 refine 仓库为佐证环境,深入这两个slice()版本。

二、JS 数组切片:Array.prototype.slice

Array.prototype.slice用于从已有数组中提取一段连续的子数组。它接受两个可选参数:start和end指示符,也可以完全不传参,因此有三种调用签名:

// 方法签名 slice(); slice(start); slice(start, end);

2.1 基础用法:从源数组提取连续区段

一个典型场景是从源数组中产出前三个、后三个或某个区间的元素:

const elements = [ "Please", "Send", "Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked", ]; const firstThree = elements.slice(0, 3); const lastThree = elements.slice(-3, elements.length); const fromThirdToFifth = elements.slice(2, 5);

slice()的两个参数都是从零开始的索引号,负值表示从数组末尾往前数的偏移。第一个参数是切片的起始索引(如firstThree中的0);第二个参数是提取应在其之前停止的索引(如3)。

console.log(firstThree); // ["Please", "Send", "Cats"] console.log(lastThree); // ["Make", "Sure", "Padlocked"] console.log(fromThirdToFifth); // ["Cats", "Monkeys", "And"]

注意第二个参数所指向的元素不包含在切片结果中,这是slice()最容易被误解的语义。

2.2slice与splice的区别

Array.prototype.slice的第二个参数不是要提取的项数。而在Array.prototype.splice中,第二个参数是要删除的项数。两者本质差异在于:splice()会原地修改原数组,而slice()返回一份副本,不触碰原数组。

2.3 参数边界行为详解

以下各小节用示例覆盖Array.prototype.slice在不同参数取值下的行为。

无参调用:整体浅拷贝

不传任何参数时,得到源数组的浅拷贝:

const allCopied = elements.slice(); console.log(allCopied); // (12) ["Please", "Send", "Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked"]

它等价于[...elements]。

省略第二个参数:一直取到末尾

不传第二个参数时,切片一直延伸到最后一个元素:

const fromThird = elements.slice(2); const lastThree = elements.slice(-3, elements.length); const lastThreeWithNoSecArg = elements.slice(-3); console.log(fromThird); // (10) ["Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked"] console.log(lastThree); // (3) ["Make", "Sure", "Padlocked"] console.log(lastThreeWithNoSecArg); // (3) ["Make", "Sure", "Padlocked"]

可以看到lastThreeWithNoSecArg与lastThree结果一致,只是少传了第二个参数。

负偏移

两个参数都支持负数,表示从数组末尾倒数:

const latestTwoBeforeLast = elements.slice(-3, -1); console.log(latestTwoBeforeLast); // (2) ["Make", "Sure"]

这里取到的是末尾倒数第二、第三个元素(即最后一个元素之前的两个)。

start 大于 end:返回空数组

const somewhereWeDontKnow = elements.slice(5, 2); console.log(somewhereWeDontKnow); // []

start 超过数组长度:返回空数组

const somewhereInOuterSpace = elements.slice(15, 2); console.log(somewhereInOuterSpace); // []

稀疏数组:空位也会被拷贝

如果切片区段内存在稀疏(空)项,它们会被一并复制:

const elements = [ "Please", "Send", , "Cats", , "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked", ]; const sparseItems = elements.slice(0, 6); console.log(sparseItems); // (6) [ 'Please', 'Send', <1 empty item>, 'Cats', <1 empty item>, 'Monkeys' ]

2.4 进阶:从函数参数列表构造数组

下面演示两种用Array.prototype.slice将函数参数列表构造为数组的方式。

第一种,借助 rest 参数与Function.prototype.call:

const createArray = (...args) => Array.prototype.slice.call(args); const array = createArray(1, 2, 3, 4); console.log(array); // (4) [1, 2, 3, 4]

这里先通过...args收集参数,再用Function.prototype.call以args为上下文调用Array.prototype.slice,得到一份新的数组副本。

第二种,用Function.prototype.bind做上下文绑定的“函数组合”:

const boundSlice = Function.prototype.call.bind(Array.prototype.slice); const createArray = (...args) => boundSlice(args); const array = createArray(1, 2, 3, 4); console.log(array); // (4) [1, 2, 3, 4]

这里声明了一个辅助函数boundSlice:它是Function.prototype.call通过bind绑定到数组函数对象Array.prototype.slice上的产物。因此调用boundSlice()效果等同于调用Array.prototype.slice.call——传入args数组后,slice会基于它创建一份副本。createArray()收集 rest 参数后交给boundSlice(),最终返回与入参一致的新数组。这段示例展示了上下文绑定(call/bind)如何与slice()组合出通用工具函数。

三、什么时候该用 JavaScriptslice方法

以下七个场景是slice()的典型适用面:

  1. 创建数组子集:需要取数组的一部分又不改变原数组时,slice是最合适的选择。
  2. 拷贝数组:slice()可以创建整个数组的浅拷贝,适合在副本上工作、保持原数组不变。
  3. 字符串操作:slice()同样适用于字符串,基于起止索引提取子串。
  4. 创建分页数据:实现分页时,用slice()取出当前页应展示的数据子集。refine 正是这么做的(见第五节源码示例)。
  5. 条件移除元素:slice()本身不移除元素,但可以与其他方法组合实现条件移除。
  6. 非破坏性数组操作:需要保证原数据结构不被修改时,slice()优于会原地修改数组的splice()。
  7. 处理arguments对象:在函数内部将类数组的arguments对象转换为真正的数组,从而可以使用数组方法。

四、JS 字符串切片:String.prototype.slice

String.prototype.slice做的事情与Array.prototype.slice完全相同,只是操作对象是字符串中的字符。它同样接受两个可选参数start和end,也可以无参调用:

// 方法签名 slice(); slice(start); slice(start, end);

4.1 基础用法:提取子串

String.prototype.slice可以直接在字符串上工作,省去了先用String.prototype.split把字符串拆成数组的麻烦:

const mnemonic = "Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"; const firstThreeChars = mnemonic.slice(0, 3); const lastThreeChars = mnemonic.slice(-3, mnemonic.length); const fromThirdToFifthChars = mnemonic.slice(2, 5); console.log(firstThreeChars); // "Ple" console.log(lastThreeChars); // "ked" console.log(fromThirdToFifthChars); // "eas"

两个参数同样是零基索引或偏移值:第一个参数是起始位置,第二个参数表示提取应在其之前停止。refine 源码中这一“首尾分离”模式随处可见,例如 humanizeString 工具函数 用text.charAt(0).toUpperCase() + text.slice(1)把驼峰/下划线命名转为首字母大写的可读文本。

4.2 字符串 slice 的边界行为

无参调用:复制整个字符串

const mnemonic = "Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"; const memorizedMnemonic = mnemonic.slice(); console.log(memorizedMnemonic); // "Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"

省略第二个参数:一直取到末尾

const charsFromThird = mnemonic.slice(2); console.log(charsFromThird); // "ease Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"

负偏移

与数组版本一致,负值表示从字符串末尾倒数:

const mnemonic = "Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"; const lastThreeChars = mnemonic.slice(-3); const latestTwoCharsBeforeLast = mnemonic.slice(-3, -1); console.log(lastThreeChars); // "ked" console.log(latestTwoCharsBeforeLast); // "ke"

start 大于 end 或超过长度:返回空字符串

const inAnotherDimension = mnemonic.slice(5, 2); const doingStringTheory = mnemonic.slice(15, 2); console.log(inAnotherDimension); // "" console.log(doingStringTheory); // ""

五、slice()的高级用例

除常规用法外,slice()在更复杂的数据场景中有几类有意思的用法。

5.1 嵌套数组的选择性切片

处理嵌套数组、只想抽取某段子数组时,slice()不触碰原结构:

const nestedArray = [ [1, 2, 3], [4, 5, 6], [7, 8, 9], ]; const slicedSubarray = nestedArray .slice(0, 2) .map((innerArr) => innerArr.slice(1)); console.log(slicedSubarray); // [[2, 3], [5, 6]]

外层先取前两行,再对每个内层数组做切片,选择性强、原数组不受影响。

5.2 处理类数组对象

类数组对象(如函数arguments、DOM 中的NodeList)默认没有数组方法。用slice()可以直接把它们变成真正的数组,省去额外的循环或转换:

function getArgumentsArray() { return Array.prototype.slice.call(arguments); } const argsArray = getArgumentsArray(1, 2, 3); console.log(argsArray); // [1, 2, 3]

5.3 配合不可变性(Redux 等场景)

在依赖不可变性的库(如 Redux)中,用slice()浅拷贝数组、在副本上做任意修改而保持原数据不变,是让状态更新可预测的常见手段:

const originalArray = [1, 2, 3, 4, 5]; const modifiedArray = originalArray.slice(0, 3).concat([10, 11]); console.log(modifiedArray); // [1, 2, 3, 10, 11] console.log(originalArray); // [1, 2, 3, 4, 5]

这类用法让代码更整洁、数据流更可信赖。

六、使用slice()的最佳实践

用slice()保障不可变性

需要拷贝数组而不触碰原数组时,slice()是首选。例如 Redux store 中的 state 切片:

const originalArr = [1, 2, 3]; const copyArr = originalArr.slice(); copyArr.push(4); console.log(originalArr); // [1, 2, 3] console.log(copyArr); // [1, 2, 3, 4]

与其他数组方法组合

slice()与map()、filter()等方法配合,可以只修改或提取数组的一部分而不影响整体:

const arr = [1, 2, 3, 4, 5]; const modifiedSubset = arr.slice(1, 4).map((num) => num * 2); console.log(modifiedSubset); // [4, 6, 8]

这种“先切片再变换”的组合尤其适合从大数组中构造视图或分页数据集。

小心处理稀疏数组

slice()会保留空(稀疏)槽位,面对不完整数据集时可能产生意外行为。若想去掉空项,可以组合filter(Boolean):

const sparseArr = [1, , 3, , 5]; const cleanArr = sparseArr.slice().filter(Boolean); console.log(cleanArr); // [1, 3, 5]

这样数组没有空洞,处理和展示都更一致。

七、refine 源码中的slice()实战印证

以下示例均取自 refine 仓库的源码,展示slice()在真实工程中的四种典型应用形态。

7.1 客户端分页:useList 的核心切片

refine 的核心数据 HookuseList在客户端分页模式下,正是用slice()从服务端返回的全量数据中切出当前页。useList.ts 中的关键逻辑:

if (prefferedPagination.mode === "client") { data = { ...data, data: data.data.slice( (prefferedPagination.currentPage - 1) * prefferedPagination.pageSize, prefferedPagination.currentPage * prefferedPagination.pageSize, ), total: data.total, }; }

这里start = (currentPage - 1) * pageSize、end = currentPage * pageSize,是本文“创建分页数据”场景的标准公式。注意它保留了total,说明切片只影响展示子集而不影响总数统计。

7.2 数据导出时的行数截断

refine 的导出逻辑(useExport)在分批拉取数据时,用slice(0, maxItemCount)把已累积的原始数据截断到导出上限。export/index.ts:

if (maxItemCount && rawData.length >= maxItemCount) { rawData = rawData.slice(0, maxItemCount); preparingData = false; }

这是slice(0, n)“取前 n 项”语义的直接应用,且截断的是重新赋值的副本,不影响分页循环本身。

7.3 路由参数解析:字符串slice(1)

refine 的路由辅助函数 pick-route-params.ts 从路由字符串(如/users/:id/posts/:postId)中提取参数名,参数段以:开头,用s.slice(1)剥掉前缀:

return segments.flatMap((s) => { if (isParameter(s)) { return [s.slice(1)]; } return []; });

这与第四节“提取子串”的模式完全一致:字符串版本的slice()避免了把路由拆成字符数组再处理的开销。

7.4 生成代码中的文本截断与 CLI 段落切片

  • refine 的代码生成器inferencer在生成 Ant Design 列表页时,对 Markdown 字段渲染直接输出value.slice(0, 80) + "..."的截断逻辑,见 inferencers/antd/list.tsx——这是slice(0, n)用于控制 UI 文本长度的实例。
  • CLI 包的公告解析 announcement/index.tsx 用sections.slice(1)跳过split产生的第一个空段:
return sections .slice(1) .map((section) => `${ANNOUNCEMENT_DELIMITER}${section}`);

这些实现印证了本文的结论:slice()是管理面板类框架中分页、截断、解析和不可变处理的基础工具。

八、小结

本文系统梳理了 JavaScript 的slice()方法:

  • 两个同构实现:Array.prototype.slice与String.prototype.slice语义一致,均接受可选的start、end参数,均返回源对象的副本,用于提取一段连续区段;
  • 参数语义:零基索引、end指向元素被排除、负值从末尾倒数、start > end或越界返回空数组/空字符串、稀疏槽位会被保留;
  • 与splice的本质区别:slice()非破坏性,splice()原地修改且第二个参数为删除项数;
  • 进阶能力:结合Function.prototype.call/bind可把slice变成从参数列表构造数组的工具;
  • 工程落地:在 refine 中,slice()直接支撑了 useList 客户端分页、导出行数截断、路由参数解析 与生成代码中的文本截断,是构建内部工具类应用时高频使用的数组/字符串基本功。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询