# ES6 copyWithin
# 前言
;copyWithin (opens new window) 用于浅复制数组的一部分到另一位置。
[1, 2, 3, 4, 5].copyWithin(0, 2, 4) // [3, 4, 3, 4, 5]
其中参数分别为target
(复制到的索引)、start
(开始复制的索引)、end
(结束复制的索引,不包括end
位置的元素)。
# 参数
参数start
默认值为0
,end
默认值为数组长度,若参数为负数,函数toAbsoluteIndex
会将其转换为正数。
function toAbsoluteIndex(target, len) {
return target < 0 ? len + target : Math.min(target, len)
}
Array.prototype.copyWithin = function (target, start, end) {
var len = this.length
target = toAbsoluteIndex(target, len)
start = toAbsoluteIndex(start || 0, len)
end = end === undefined ? len : toAbsoluteIndex(end, len)
var count = Math.min(len - target, end - start)
var inc = 1
while (count--) {
if (start in this) {
this[target] = this[start]
}
target += inc
start += inc
}
return this
}
# 移动次数
;count
为移动次数,取值分为两种情况,第一种由length
和target
决定。
[1, 2, 3, 4, 5].copyWithin(-2) // [1, 2, 3, 1, 2]
以上相当于copyWithin(3, 0, 5)
,其中length - target = 2
,end - start = 5
,移动次数为两者较小值2
。
另外一种情况由end
和start
决定。
[1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5]
;length - target = 5
,end - start = 1
,移动次数为两者较小值1
。
故移动次数为Math.min(length - target, end - start)
。
# 类数组
;copyWithin
对于类数组也是适用的。
[].copyWithin.call({length: 5, 3: 1}, 0, 3) // {0: 1, 3: 1, length: 5}
对象仅含有此属性时,才复制到对应位置。
if (start in this) {
this[target] = this[start]
}
注意只能用in
操作符判断,不能用this[start] !== undefined
来判断,原因在于可能属性值就是undefined
。
const foo = {}
const bar = { key: undefined }
foo.key // undefined
'key' in foo // false
bar.key // undefined
'key' in bar // true
# 倒序移动
以上代码运行如下示例,输出[1, 2, 3, 3, 3, 6]
,并不是正确的结果。
[1, 2, 3, 4, 5, 6].copyWithin(3, 2, 4) // [1, 2, 3, 3, 4, 6]
我们将start
和end
固定为2
和4
,看看不同target
值时的情况。
start | end | count | target | start < target | target < start + count | 输出结果 | 正确结果 | 一致 |
---|---|---|---|---|---|---|---|---|
2 | 4 | 2 | 0 | 否 | 是 | [3, 4, 3, 4, 5, 6] | [3, 4, 3, 4, 5, 6] | 是 |
2 | 4 | 2 | 1 | 否 | 是 | [1, 3, 4, 4, 5, 6] | [1, 3, 4, 4, 5, 6] | 是 |
2 | 4 | 2 | 2 | 否 | 是 | [1, 2, 3, 4, 5, 6] | [1, 2, 3, 4, 5, 6] | 是 |
2 | 4 | 2 | 3 | 是 | 是 | [1, 2, 3, 3, 3, 6] | [1, 2, 3, 3, 4, 6] | 否 |
2 | 4 | 2 | 4 | 是 | 否 | [1, 2, 3, 4, 3, 4] | [1, 2, 3, 4, 3, 4] | 是 |
2 | 4 | 2 | 5 | 是 | 否 | [1, 2, 3, 4, 5, 3] | [1, 2, 3, 4, 5, 3] | 是 |
可以发现,唯独运行copyWithin(3, 2, 4)
时结果错误。
原因在于移动过程中前面的元素会覆盖后面的元素。
倒序移动问题将得到解决。
注意仅有start < target
和target < start + count
都满足时,才倒序移动,剩余情况还是正常移动。另外target
应该从target + count - 1
递减,例如以上target
应该从索引为4
(3 + 2 - 1
)的位置开始,start
也同理。
if (start < target && target < start + count) {
inc = -1
target += count - 1
start += count - 1
}
# ES5 兼容
function toAbsoluteIndex(target, len) {
return target < 0 ? len + target : Math.min(target, len)
}
Array.prototype.copyWithin = function (target, start, end) {
var len = this.length
target = toAbsoluteIndex(target, len)
start = toAbsoluteIndex(start || 0, len)
end = end === undefined ? len : toAbsoluteIndex(end, len)
var count = Math.min(len - target, end - start)
var inc = 1
if (start < target && target < start + count) {
inc = -1
target += count - 1
start += count - 1
}
while (count--) {
if (start in this) {
this[target] = this[start]
}
target += inc
start += inc
}
return this
}
# 🎉 写在最后
🍻伙伴们,如果你已经看到了这里,觉得这篇文章有帮助到你的话不妨点赞👍或 Star (opens new window) ✨支持一下哦!
手动码字,如有错误,欢迎在评论区指正💬~
你的支持就是我更新的最大动力💪~
GitHub (opens new window) / Gitee (opens new window)、GitHub Pages (opens new window)、掘金 (opens new window)、CSDN (opens new window) 同步更新,欢迎关注😉~