# 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默认值为0end默认值为数组长度,若参数为负数,函数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为移动次数,取值分为两种情况,第一种由lengthtarget决定。

[1, 2, 3, 4, 5].copyWithin(-2) // [1, 2, 3, 1, 2]

  以上相当于copyWithin(3, 0, 5),其中length - target = 2end - start = 5,移动次数为两者较小值2

  另外一种情况由endstart决定。

[1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5]             

  ;length - target = 5end - 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]

  我们将startend固定为24,看看不同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 < targettarget < start + count都满足时,才倒序移动,剩余情况还是正常移动。另外target应该从target + count - 1递减,例如以上target应该从索引为43 + 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) 同步更新,欢迎关注😉~

最后更新时间: 4/10/2022, 2:30:10 PM