关于 slice、splice 记忆和区分

简介: 关于 slice、splice 记忆和区分

前言


你是不是也很烦,记不住 slice()splice() 的用法,隔一段时间,再用时就得翻文档。比如说:

  • 哪个会改变原数组,哪个不会?
  • 对数组进行操作,包不包括要修改的最后一项呢?
  • 怎样区分会更容易记忆?

本文将会介绍这些内容,如果没时间或直接看结论的,请滑动鼠标至文末。


正文


一、Array.prototype.slice


slice() 不会“改变”原数组。

语法如下:

array.slice([begin[, end])


beginend 都是可选的。返回一个新的数组,是由 beginend 决定的原数组的浅拷贝(包括 begin,不包括 end )。通俗地讲,就是截取原数组的一部分,并返回截取部分,且“不改变”原数组


总结一下:


  • 返回新数组,且不改变原始值,但注意引用值的问题。
  • 返回结果为 [begin, end),用数学话术就是左闭右开区间,即含 begin,但不包括 end
  • beginend 的值可为 负数0正数。其中 0 和正数不多说,若为负数,从数组末尾开始确定索引值,其中 -1 为数组最后一个元素,以此类推。若负数小于 -length,则索引值为 0(可在内心转换为正向索引值)。
  • begin ≥ arr.length(超出数组范围)或 begin ≥ end(两者翻译成正向索引后再比较),不会报错,会返回空数组([])。
  • 若参数 beginend 不为数值,会自动隐式类型转换为 Number 类型,再截取。
  • Array.prototype.slice() 可用来将一个类数组(array-like)转换为新数组。

请看示例:

const arr = [1, 2, 3, 4, 5]
const arr2 = [1, 2, { num: 3 }, 4, 5]
// 1️⃣ 当 begin 和 end 同时缺省时,会“浅拷贝”一个完整的数组
//    1)适合拷贝一个完整无引用值的数组
//    2)将类数组转换为数组,如 arguments -> array
arr !== arr.slice() // true,浅拷贝效果
function foo() {
  // 类数组不具有数组的任何方法,转换后 arr 就变成了真正的数组
  // Tips: 但现在用得更多的可能是 rest 参数
  const arr = Array.prototype.slice.call(arguments) // 或 [].slice.call(arguments)
}
// 2️⃣ 返回数组的一部分
arr.slice(4) // [5]
arr.slice(1, 3) // [2, 3]
arr.slice(-2, -1) // [4]
// 3️⃣ 返回空数组
arr.slice(1, 1) // [],属于 begin = end,但不包含 end
arr.slice(3, -4) // [],属于 begin ≥ end 的情况
arr.slice(5) // [],属于超出数组范围的情况
// 4️⃣ 浅拷贝引用值的问题,其实非常简单,顺便提一下而已
const newArr = arr2.slice(2) // [{ num: 3 }, 4, 5]
newArr[0].num = 6 // 修改新数组的引用值
arr2[2] // { num: 6 },会影响原数组
// 5️⃣ 若参数不为数值,将会发生隐式类型转换,再截取
arr.slice(true) // [2, 3, 4, 5],true 会转换为 Number 类型为 1
arr.slice(false) // [1, 2, 3, 4, 5],同理
arr.slice('str') // [1, 2, 3, 4, 5],字符串 'str' 会被转换为 NaN,效果同 arr.slice(NaN)
// 6️⃣ 若参数为小数,应该是采用 parseInt() 取整数部分
arr.slice(1, 3.6) // [2, 3]
arr.slice(-2.3) // [4, 5]


还有,


字符串也有一个类似的方法:String.prototype.slice(),它用来提取字符串的某一部分,并返回一个新的字符串,且不会改变原字符串。语法如下:

// 从原字符串中,截取 [beginIndex, endIndex) 的字符串
string.slice(beginIndex, endIndex)

应用场景:

const str = 'string'
// 1️⃣ 截取字符串
str.slice(1, 3) // "tr"
// 2️⃣ 相信你也见过以下用法,常用来生成随机字符串
const randomKey = Math.random().toString(36).slice(2)


二、Array.prototype.splice


讲真的,它跟 slice() 长得像不说,还容易混淆,隔一段时间不用,都得翻一下文档确认一下。


slice() 不同,splice() 会改变原数组。

语法如下:

array.splice([start[, deleteCount[, item1[, item2[, ...]]]]])

所有参数都是可选的。从 MDN 上的表述看,参数 start 应该不能缺省的,但实际并不会报错,因此可认为是全可选的。splice() 返回被删除的元素所组成的一个新数组,若没有删除,则返回空数组([]

  • start - 开始位置(从 0 计数)
  • start ≥ length,即超出数组长度,则从数组末尾添加内容;
  • start < 0,即为负数,则从数组末尾开始的第几位开始,负数小于 -length 时,从 0 开始。计数方式与 slice() 相同。
  • deleteCount - 整数,表示要移除的数组元素的个数。
  • 若缺省 deleteCount 或者 deleteCount 大于 start 之后所有元素数量,则删除 start 后面的所有元素,含 start
  • deleteCount ≤ 0,此时不移除元素。这种情况下,常用来插入新元素。
  • item1, item2, ... - 要添加进数组的元素,从 start 位置开始。
    若不指定,则 splice() 将只删除数组元素。


简单总结一下:


  • splice() 会改变原数组,并返回一个被删除元素组成的数组。
  • splice() 参数 start 接受负数、0、正数,计算起始位置与 slice() 方法一致。
  • deleteCount 为正数时,splice(start, deleteCount) 所删除的元素包含 [start, start + deleteCount),这里的 start 是指转换后正向索引值。
  • deleteCount 为负数或零,则不删除元素。常用于不删除原数组元素,并插入新元素的场景。
  • 请注意,deleteCount 是指删除的个数,而非索引值。我想这也就是 slice()splice() 容易混淆的原因所在。
  • splice() 中的 startdelectCount 也是会发生隐式类型转换的。

请看示例:

const arr = [1, 2, 3, 4, 5]
// 1️⃣ 允许缺省所有参数,但没有真正的意义
arr.splice() // [],原数组也没有改变
// 2️⃣ 删除,并插入新元素
arr.splice(1, 2, 6) // [2, 3],此时 arr 被修改为 [1, 6, 4, 5]
arr.splice(-2, 3, 6) // [4, 5],此时 arr 被修改为 [1, 2, 3, 6]
// 3️⃣ 删除 start 后面所有元素
arr.splice(1) // [2, 3, 4, 5],此时 arr 被修改为 [1]
arr.splice(-100) // [1, 2, 3, 4, 5],此时 arr 被修改为 []
// 4️⃣ 不删除原数组元素,并插入一个或多个新元素
//    这个应该就是 splice() 用得最多的场景吧!
arr.splice(1, 0, 6) // [],此时 arr 被修改为 [1, 6, 2, 3, 4, 5]
arr.splice(1, 0, 6, true) // [],此时 arr 被修改为 [1, 6, true, 2, 3, 4, 5]
// 5️⃣ 隐式类型转换
arr.splice(true, true) // [2],相当于 arr.splice(1, 1)
// ⚠️ 请注意,以上每条语句是基于 arr 为 [1, 2, 3, 4, 5] 得出的结果,
// 并不是按顺序执行得出的结果,就怕有人误解。因为 splice() 方法是会修改原数组的。


三、String.prototype.split


顺道提一下,其实 split() 这个就很简单了,常用于字符串转为数组、解析 URL 参数等场景。

在字符串与数字切换,常用到 String.prototype.split()Array.prototype.join()Array.prototype.reverse() 方法。

语法如下:

str.split([separator[, limit]])


参数 separatorlimit 都是可选的。若缺省 separator 时,返回的数组包含一个由整个字符串组成的元素。而 limit 的作用是返回分割片段的数量。

const str = 'hello'
// 字符串分割
str.split() // ["hello"]
str.split('') // ["h", "e", "l", "l", "o"]
str.split('', 2) // ["h", "e"]
// 字符串与数字转换
str.split('').join(',') // "h,e,l,l,o"
// 也常用来反转字符串
str.split('').reverse().join('') // "olleh"


separator 可以是字符串,也可以为正则表达式,它适合提取一些不太规则的字符串。

假设有以下两个字符串,我们要把月份提取出来,并返回数组:

const str1 = 'Jan; March; April; June'
const str2 = 'Jan ;March ; April; June'
// 对于 str1 是相对比较规律的,我们可以直接
str1.split('; ') // ["Jan", "March", "April", "June"]
// 而 str2 就不能通过上述方式去提取了,可以使用正则表达式作为 separator 参数
const re = /\s*;\s*/g
str2.split(re) // ["Jan", "March", "April", "June"]


四、总结一下:


本文,主要是讲解 slice()splice() 方法及其区别。好吧,面试官也喜欢问这俩货。

  1. 从参数上区分:slice(begin, end)splice(start, deleteCount, ...item) 两个方法:
  • slice()beginend 都指原数组对应的索引值。
  • splice() 中只有 start 是指原数组对应的索引值,deleteCount 是要删除的数量。如果要转化为 [begin, end) 的形式,先将 start 转为正向索引值(如 -1 转为原始值最后一项的索引值),然后要删除的区间自然就是 [start, start + deleteCount)
  • slice() 缺省 endsplice() 缺省 deleteCount,它们都是会截取或删除 start 之后的所有元素,且包含 beginstart
  1. 从是否改变原数组来区分:slice() 不会改变原数组,而 splice() 会改变原数组。从这点上看,它们适合应用以下场景:
  • slice() 适合用于浅拷贝数组,在不改变原数组的前提下,拷贝原数组的一部分或整个数组。
  • splice() 适合在数组中插入新元素。
  1. 是否截取(或删除)起始项、终止项的问题,换个角度起始很容易区分。在实际场景中,起始项或终止项设为 负数,也是很常见的。我们只要在内心将其翻译为“正向”的索引值即可,比如 -1 表示数组最后一个元素,那它的索引值就是 length - 1,以此类推。当 负数 值超出 -length正数 值超出 length 时,要么从 0 开始或 数值末尾 开始。
  • slice() 会截取 [begin, end) 区间的元素。
  • splice() 会删除 [start, start + deleteCount) 区间的元素。
  1. 从数学的区间角度去看,这个就很容易区分了。


五、References



目录
相关文章
|
数据采集 JSON JavaScript
Python爬虫案例:抓取猫眼电影排行榜
python爬取猫眼电影排行榜数据分析,实战。(正则表达式,xpath,beautifulsoup)【2月更文挑战第11天】
635 2
Python爬虫案例:抓取猫眼电影排行榜
|
3月前
|
机器学习/深度学习 人工智能 搜索推荐
从零构建短视频推荐系统:双塔算法架构解析与代码实现
短视频推荐看似“读心”,实则依赖双塔推荐系统:用户塔与物品塔分别将行为与内容编码为向量,通过相似度匹配实现精准推送。本文解析其架构原理、技术实现与工程挑战,揭秘抖音等平台如何用AI抓住你的注意力。
728 7
从零构建短视频推荐系统:双塔算法架构解析与代码实现
|
9月前
|
存储 安全 索引
回收站删除的照片怎么恢复?
在日常使用电脑的过程中,我们常常会不小心误删照片、文件或者其他重要数据,尤其是在清空回收站后,许多人会感到恐慌,担心数据永远丢失。不过,实际上,即使回收站中的照片被删除,也并非完全没有恢复的可能。本文将详细介绍几种常用的照片恢复方法,帮助大家在遇到类似问题时能够及时采取措施,尽可能地找回丢失的数据。
|
Java 应用服务中间件 测试技术
Maven学习笔记(一):Maven基础(基于命令行的学习和应用)
Maven 是一款 Java 项目构建工具,主要用于管理 jar 包及其依赖关系。 本文主要了解Maven基础知识及基础应用,旨在为之后的进一步学习奠定基础。 内容上几近全为学习《尚硅谷2022版Maven教程》整理所得。 仅供参考。
823 82
Maven学习笔记(一):Maven基础(基于命令行的学习和应用)
|
移动开发 JavaScript 安全
js的常见的三种密码加密方式-MD5加密、Base64加密和解密和sha1加密详解总结
js的常见的三种密码加密方式-MD5加密、Base64加密和解密和sha1加密详解总结
806 0
|
缓存 前端开发 Go
go中的chan管道机制
Go 语言推崇通过通信来共享内存而非共享内存来通信,其中 Channel(通常简写为 `chan`)作为关键机制之一,允许两个并发执行的协程之间进行同步和数据交换。`chan` 是一种引用类型,可通过 `make` 函数创建,
249 2
|
存储 运维 安全
阿里云OSS的优势
【7月更文挑战第19天】阿里云OSS的优势
718 2
|
Linux 开发者 iOS开发
Python常用打包工具比较
以上是常用的四种打包工具比较。各自有着自己的优缺点,开发者可以根据自己的需求来选择合适的工具。如果你只需要在 Windows 平台上运行应用程序,可以选择 py2exe。如果你需要跨平台支持,并且希望打包过程简单,可以选择 Briefcase。如果你需要支持多个平台,并且打包过程比较复杂,可以选择 cx_Freeze。如果你需要支持多个平台,并且对第三方库的兼容性有较高的要求,可以选择 PyInstaller。
1429 4
vue2 插槽(默认插槽 slot 、具名插槽 v-slot 、作用域插槽 slot-scope -- 插槽传值 )
vue2 插槽(默认插槽 slot 、具名插槽 v-slot 、作用域插槽 slot-scope -- 插槽传值 )
3021 1