这篇文章发布于 2022年12月28日,星期三,21:48,归类于 JS API。 阅读 10554 次, 今日 6 次 3 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=10667 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。
一、copyWithin()对比slice()
JavaScript中数组的copyWithin()和slice()方法作用都是复制数组,且都是浅复制。
区别之一在于
copyWithin()
是将数组中的一部分复制并替换另外一部分,总长度是不变的,而slice()
方法只复制,不替换,返回的数组长度是由复制的数组项目个数决定的。
看一个区别,已知数组 arr
为 [1, 'A', '甲', 'I']
,则分别执行 arr.copyWithin(2, 3)
和 arr.slice(2, 3)
会有如下所示的不同结果。
arr = [1, 'A', '甲', 'I']; console.log(arr.copyWithin(2, 3)); // 结果是: [1, 'A', 'I', 'I']
arr = [1, 'A', '甲', 'I']; console.log(arr.slice(2, 3)); // 结果是: ['甲']
在控制台运行结果如下图所示:
区别之二在于
copyWithin()
方法会改变原始的数组,而slice()
方法并不会。
例如:
arr = [1, 'A', '甲', 'I']; arr.copyWithin(2, 3); console.log(arr); // 结果是: [1, 'A', 'I', 'I']
arr = [1, 'A', '甲', 'I']; arr.slice(2, 3); console.log(arr); // 结果是:[1, 'A', '甲', 'I']
控制台运行结果如下图所示:
可以看到,copyWithin()
方法执行后,原始的arr数组发生了变化,变成了复制后的数组内容。
区别之三在于
slice()
复制对于字符串也是有效的,但是copyWithin()
方法却不支持。
例如:
// 返回值是'angx' ('zhangxinxu').slice(2, 6)
但是下面的语句就会报错:
// 会无情报错 ('zhangxinxu').copyWithin(2, 6)
二、copyWithin()的语法
copyWithin()
的语法形式上不算复杂,但是理解起来有些费脑细胞,我也是看了好几遍才弄明白。
语法
Array.prototype.copyWithin(index, start, end)
表示复制start-end的内容,并从index开始替换。
使用示意:
[].copyWithin() [].copyWithin(index) [].copyWithin(index, start) [].copyWithin(index, start, end)
参数
- index
- 复制的数组项开始替换的起始索引值。
- start
- 复制的数组项的起始位置(包含)。
- end
- 复制的数组项的结束位置(不包含)。
举例说明
例如:
arr = [1, 'A', '甲', 'I', '一']; arr.copyWithin(2, 3, 4);
此时,start=3,end=4,表示复制数组的arr[3](包含)到arr[4]项(不包含),因此复制的值是 arr[3],也就是 ‘I’。
index=2,则表示复制的值从arr[2]这里开始替换。
于是复制的结果就是:[1, ‘A’, ‘I’, ‘I’, ‘一’]
参数细节
copyWithin()
方法的参数细节比较多,毕竟数值有大有小,有正有负,情况不同,规则也会不同。
具体如下:
1. index参数
index
参数不设置也是合法的,不过你不设置该参数数组就会原路复制返回,类似于设置为 0,这样处理只会浪费性能,没有任何实际意义。因此,
index
参数可以认为是必须参数。- 如果index是小数,则会按照整数处理,并且是向下取整。例如:
// 返回值是 true [1, 2, 3].copyWithin(1.5).join() == [1, 2, 3].copyWithin(1).join() // copyWithin(1.5)执行结果是:[1, 1, 2]
- 如果index是负数。
- 如果index负数的绝对值很大,甚至比数组的长度还要大,则此时 index 当作 0 处理。例如:
// 返回值是 ['zhang', 'xin', 'xu'] // 等同于 copyWithin(0) ['zhang', 'xin', 'xu'].copyWithin(-999)
- 否则,最终的 index 值等于 index + arr.length,比方说
[1, 2, 3].copyWithin(-1)
就等同于[1, 2, 3].copyWithin(2)
;// 结果是[1, 2, 1] [1, 2, 3].copyWithin(-1); // 结果同样是[1, 2, 1] [1, 2, 3].copyWithin(2);
- 如果index负数的绝对值很大,甚至比数组的长度还要大,则此时 index 当作 0 处理。例如:
- 如果index的值超出了数组的长度范围,则不会有任何内容被复制。这里需要注意下,没有任何内容复制和所有内容都复制是有区别的,虽然看起来返回的都像是原数组,但其实并不是,性能这块是有着明显的差异的。
我们可以用非常简单的方法测试下,例如我们构造一个99万个项目的数组进行复制处理:
console.time('start'); arr = new Array(999999); console.timeLog('start'); arr.copyWithin(0); console.timeLog('start'); arr.copyWithin(1000000); console.timeEnd('start');
我们在控制台跑一下上面的代码,通过观察执行时间就会发现执行copyWithin(0)用了近70ms,而执行copyWithin(1000000)的用时几乎就是0.
- 如果 index 的位置在 start 之后,则复制的内容粘贴到数组长度的尾部就停止了,也就是不会增加数组的长度的:
例如:
// 结果是 [1, 'A', '甲', 'A', '甲'] [1, 'A', '甲', 'I', '一'].copyWithin(3, 1);
2. start参数
- start参数是可选的,如果不设置,则当作0处理,表示数组从头复制到尾部(index小于数值长度的前提下);
- start如果是小数,会向下取整处理。例如
[].copyWithin(0, 1.9999)
等同于[].copyWithin(0, 1)
; - start如果是负数,则从数组的后面开始取值,等同于 start + arr.length;
- start如果是负数,同时绝对值小于 -1 * arr.length,则当作 0 处理。
- start如果超出数组的长度范围,则不会发生复制。
2. end参数
- end参数是可选的,如果不设置,则当作
array.length
处理,表示数组复制到尾部; - end如果是小数,会向下取整处理。例如
[].copyWithin(0, 1, 2.999)
等同于[].copyWithin(0, 1, 2)
,例如:// 结果是:[2, 2, 3, 4] [1, 2, 3, 4].copyWithin(0, 1, 2.9999)
- end如果是负数,则从数组的后面开始取值,等同于 end + arr.length;
- end如果是负数,同时绝对值小于 -1 * arr.length,则当作 0 处理。
- end如果超出数组的长度范围,则当作
array.length
处理。 - 如果end对应的位置比start小,则不会发生复制。
三、copyWithin实际应用
我想的一些应用,也找了一些应用, 欢迎大家补充更适合使用copyWithin()
的场景。
1. 删除数组中间某一项
数组删除第一个项目可以使用 shift() 方法,删除最后一个项目可以使用 pop() 方法,但似乎没有删除中间某一项的方法。
在过去都是使用 splice() 方法实现的,语法示意:
[].splice(index, 1);
现在多了个选择,使用 copyWithin()
,例如:
[].copyWithin(index, index + 1).pop();
例如,删除数组 arr 中的 第 2 项:
arr = ['甲', '乙', '丙', '丁']; arr.copyWithin(1, 2).pop(); // 结果是['甲', '丙', '丁'] console.log(arr);
2. 置顶数组某一项
这种数组总长度不变的场景比较适合 copyWithin 方法:
// 自定义top置顶方法 Array.prototype.top = function (index) { const value = this[index]; this.copyWithin(1, 0, index); this[0] = value; };
例如:
arr = ['甲', '乙', '丙', '丁']; arr.top(1); // 结果是["乙", "甲", "丙", "丁"] console.log(arr);
3. 模拟插入排序算法
下面这个案例源自这个stackoverflow:
const insertionSort = (data, compare) => { const arr = [...data]; let unsort_index = 1; while (unsort_index < arr.length) { while (arr[unsort_index] >= arr[unsort_index - 1]) unsort_index += 1; const pick = arr[unsort_index]; let iter = 0; while (iter < unsort_index && arr[iter] < pick) iter += 1; arr.copyWithin(iter + 1, iter, unsort_index); arr[iter] = pick; unsort_index += 1; } return arr; } const input = [2, 3, 5, 1, 9, 8, 6, 6]; const asc = (a, b) => a - b; const dsc = (a, b) => b - a; console.log({ input, asc_sorted: insertionSort(input, asc) }); console.log({ input, dsc_sorted: insertionSort(input, dsc) });
等。
四、结束语
copyWithin()
是一个非常高性能的数组移动方法。
不过,我查找了一圈,暂时没有发现特别适合这个方法的应用场景,理论上,这个仅适合当前数据内复制并粘贴的场景。
比方说一张画布内复制粘贴,对于在数据层面,这样的场景并不多,因为直接覆盖数据,还是用旁边的数据覆盖,并不常见。
也可能是自己目前所接触的开发场景还不够多。
希望以后可以碰到特别适合使用 copyWithin()
方法使用的场景。
好了,以上就是本文的全部内容了。
如果你觉得写得还不错,欢迎转发!
?
本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=10667
(本篇完)
- Object.is/===、数组at/直接索引、substring/slice的区别 (0.746)
- 实用的JS对象分组静态方法Object.groupBy() (0.299)
- 翻译:ECMAScript 5.1简介 (0.179)
- 我对原型对象中this的一个懵懂错误认识 (0.179)
- ES5中新增的Array方法详细说明 (0.179)
- 我是如何理解"Another JavaScript quiz"中的题目 (0.179)
- ES6 JavaScript Promise的感性认知 (0.075)
- HTMLUnknownElement与HTML5自定义元素的故事 (0.075)
- 简单了解ES6/ES2015 Symbol() 方法 (0.075)
- JS字符串补全方法padStart()和padEnd()简介 (0.075)
- JS WeakMap应该什么时候使用 (RANDOM - 0.075)
会用了,不过暂时没有使用场景~~
用slice和splice方法实现了一个和Array.prototype.copyWithin相同语义的函数,函数复制操作部分逻辑不复杂,对参数的处理上稍微复杂一点:
function copyWithin(arr, target, start, end = arr.length){
target=Number(target);
start=Number(start);
end=Number(end);
target = Number.isNaN(target) ? 0 : Math.trunc(target);
start = Number.isNaN(start) ? 0 : Math.trunc(start);
end = Number.isNaN(end) ? 0 : Math.trunc(end);
let length = arr.length;
target = target < 0 ? length + target : target;
target = target < 0 ? 0 : target ;
start = start < 0 ? length + start : start;
start = start < 0 ? 0 : start;
end = end length ? length : end = length || start >= length || end <= start)
return arr;
let copyLength = Math.min(end – start, length – target);
let temp = arr.slice(start, copyLength + start);
arr.splice(target, temp.length, …temp);
return arr;
}
厉害,赞一个??