先记住这个答案
copyWithin(target, start, end) 把当前数组某段范围复制到同一数组的目标位置,修改原数组并返回它,长度不变,超出尾部的复制会被截断。源范围包含 start、不包含 end,支持相对负索引。源和目标重叠时,方法按能保留预期源序列的方向执行,效果不能照搬一个随手写的正向赋值循环。它复制的是元素值或引用,不删除源项,也不是深拷贝;稀疏位置还有属性删除语义。是否更快应在实际数据类型和引擎上测量,不能只凭内建方法名称推断。
- 同一数组内部复制,长度不变
- 重叠范围有明确定义的复制方向
- 省去显式切片不等于所有场景都更快
重叠范围不能用直觉逐项覆盖
示例把零到二之前的两个值复制到索引一。若手写正向循环先改索引一,再拿已被改写的索引一覆盖二,就会得到错误的重复值;copyWithin 的重叠规则避免这个问题。
可以把预期理解为从原源片段取得对应序列再覆盖,但规范实现不要求真的分配一份中间数组。对用户而言应验证最终内容、长度和身份,避免把解释模型误写成内部实现保证。
const values = ['A', 'B', 'C', 'D'];
const returned = values.copyWithin(1, 0, 2);
console.log(JSON.stringify(values));
console.log(returned === values, values.length);
console.log(JSON.stringify([1, 2, 3, 4].copyWithin(3, 0, 3)));查看输出与解释
["A","A","B","D"]
true 4
[1,2,3,1]重叠复制得到 A、B 两个原始源值,而不是连续传播 A。第二例目标只剩一个位置,所以只覆盖最后一项,不会把数组长度扩展成六。
选择它之前确认需要覆盖而不是增删
维护固定容量缓冲或复用临时数组时,原地覆盖可能符合需求;编辑列表若要插入新记录并移动后续项,则应使用对应插入或重建操作。copyWithin 不会替业务增加空位或保留被覆盖数据。
状态管理里原数组可能被多个组件共享,原地修改会改变所有持有者看到的值。需要不可变更新时应先设计容器复制,并确认元素引用是否也需隔离,不能为了少分配而破坏更新契约。
普通数组、空位与性能要分别分析
复制对象元素时只复制引用,修改目标对象的内部字段仍可能影响源位置。普通稀疏数组若源位置不存在,对应目标属性可被删除,而不是简单写入 undefined,因此属性结构也应纳入测试。
密集数组、稀疏数组和 TypedArray 有不同约束与优化路径。热路径应比较耗时、内存和数据规模,方法可以减少手工范围处理的错误,却不能提供一个跨所有引擎的固定性能倍率。
容易答错的地方
- 以为 copyWithin 复制后源片段会消失
- 它执行覆盖复制,不是剪切移动,源值仍可能保留在原位置。若业务需要删除或重新排列记录,应先写出期望数组,再选择对应操作,避免遗漏重复项和被覆盖数据。
- 手写正向循环替换却不处理重叠
- 目标落在源范围右侧时,早期写入会改变后续读取,导致同一个值被不断传播。应根据重叠方向选择读取顺序或使用明确快照,并通过重叠与非重叠用例验证结果。
面试官还会怎么问?
copyWithin 返回新数组吗?
返回的是被修改的当前数组,长度保持原值。链式写法不会让它变成不可变操作,调用方应检查共享引用和后续排序等方法是否继续修改同一个容器。
负数 target 表示什么?
它按数组长度换算为相对位置,再受合法范围约束。实际复制数量还取决于目标到尾部的剩余空间,因此不能只看源范围长度就断言会复制全部元素。
复制后的对象元素能独立修改吗?
不能自动独立,源和目标可能指向同一对象。若需要独立状态,应在复制规则中明确创建新对象或复制必要字段,容器内部位置变化不等于对象身份发生变化。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。