logan70 / Blog

写博客的地方,觉得有用的给个Star支持一下~
81 stars 9 forks source link

语法和API - 实现数组方法(下) #42

Open logan70 opened 4 years ago

logan70 commented 4 years ago

实现数组方法(下)

所有数组方法的实现均忽略参数校验、边界条件判断,主要关注核心逻辑的实现。

部分数组方法会基于Array.prototype.reduce方法来实现,关于reduce方法的讲解及实现详见彻底搞懂数组reduce方法

Array.prototype.push()

MDN - Array.prototype.push()

push()方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。

push()方法可用于类数组对象,需要注意的是length不存在或无法转为数值时,将被设置为0,并从索引0开始添加元素。

Array.prototype._push = function(...args) {
  const len = Number(this.length) || 0
  this.length = len
  for (arg of args) {
    this[this.length++] = arg
  }
  return this.length
}

Array.prototype.reduceRight()

MDN - Array.prototype.reduceRight()

reduceRight()方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。

之前已实现过reduce()方法,详情见文章开头,我们使用其来实现reduceRight()方法。

Array.prototype._reduceRight = function(callback) {
  const len = this.length
  if (arguments.length >= 2) {
    return [...this].reverse().reduce((acc, cur, i) => {
      return callback(acc, cur, len - 1 - i, this)
    }, arguments[1])
  } else {
    return [...this].reverse().reduce((acc, cur, i) => {
      return callback(acc, cur, len - 1 - i, this)
    })
  }
}

Array.prototype.reverse()

MDN - Array.prototype.reverse()

reverse()方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。

通过交换数组左右对应位置的值实现,这样只用迭代Math.floor(this.length / 2)次。

Array.prototype._reverse = function() {
  const len = this.length
  for (let i = 0; i < Math.floor(len / 2); i++) {
    const correspondIndex = len - 1 - i;
    [this[i], this[correspondIndex]] = [this[correspondIndex], this[i]]
  }
  return this
}

Array.prototype.shift()

MDN - Array.prototype.shift()

shift()方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

Array.prototype._shift = function() {
  if (!this.length) return undefined
  // 记录第一项并删除
  const valToDel = this[0]
  delete this[0]
  // 之后项依次左移
  for (let i = 0; i < this.length - 1; i++) {
    this[i] = this[i + 1]
  }
  // 删除最后一项
  delete this[this.length - 1]
  // 修正数组长度
  this.length--
  return valToDel
}

Array.prototype.slice()

MDN - Array.prototype.slice()

slice()方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

Array.prototype._slice = function(beginIndex = 0, endIndex = this.length) {
  const begin = beginIndex < 0 ?
    Math.max(this.length + beginIndex, 0) :
    Math.min(beginIndex, this.length)
  const end = endIndex < 0 ?
    Math.max(this.length + endIndex, 0) :
    Math.min(endIndex, this.length)

  const count = end - begin
  if (count <= 0) return []

  const result = new Array(count)
  for (let i = 0; i < count; i++) {
    if (i in this) {
      result[i] = this[begin + i]
    }
  }
  return result
}

Array.prototype.some()

MDN - Array.prototype.some()

some()方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。

some()方法具有短路特性,且会跳过数组稀疏项。

Array.prototype._some = function(callback, thisArg) {
  for (let i = 0; i < this.length; i++) {
    if (i in this && callback.call(thisArg, this[i], i, this)) {
      return true
    }
  }
  return false
}

Array.prototype.splice()

MDN - Array.prototype.splice()

splice()方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

Array.prototype._splice = function(startIndex, deleteCount = this.length - startIndex, ...items) {
  const len = this.length
  const start = startIndex < 0 ?
    Math.max(len + startIndex, 0) :
    Math.min(len, startIndex)

  const realDeleteCount = deleteCount <= 0 ?
    0 :
    Math.min(len - start, deleteCount)

  const deleteVals = this.slice(start, start + realDeleteCount)
  const right = [...items, ...this.slice(start + realDeleteCount)]

  for (let i = 0; i < right.length; i++) {
    this[start + i] = right[i]
  }
  // 修正length属性,自动删除多余项
  this.length = start + right.length
  return deleteVals
}

Array.prototype.unshift()

MDN - Array.prototype.unshift()

unshift()方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。

Array.prototype._unshift = function(...items) {
  const addCount = items.length
  if (addCount === 0) return this.length
  this.length += items.length
  for (let i = this.length - 1; i >= 0; i--) {
    if (i >= addCount) {
      this[i] = this[i - addCount]
    } else {
      this[i] = items[i]
    }
  }
  return this.length
}

Array.prototype.values()

MDN - Array.prototype.values()

values()方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值,实现方法与keys()entries()相似。

Array.prototype._values = function(...items) {
  function *gen() {
    for (let i = 0; i < this.length; i++) {
      yield this[i]
    }
  }
  return gen.call(this)
}