数组各种好用API的手写

227 阅读6分钟

「回顾2022,展望2023,我正在参与2022年终总结征文大赛活动

1、前言

在我们conding中,我们都用过数组这个数据结构,我们除了使用了数组自带的增删改查方法,还会使用带数组身上各类的API方法,下面我就来介绍数组身上的各类api方法。

2、迭代属性的API方法

2.1 Array.every()

1. evrry()原理:

every() 方法接受一个回调函数作为参数,它是检查数组中的所有元素是否都通过了我们的限制条件,every() 方法对数组中存在的每个元素都会去执行一次:

  • 如果找到函数返回 false 值的数组元素,every() 返回 false,并且不检查剩余的元素
  • 如果没有出现 false,every() 返回 true
  • every() 不改变原始数组

2. evrry()方法的手写实现:

const singer = [
    { name: '111', num: 20 },
    { name: '222', num: 25 },
    { name: '333', num: 19 },
    { name: '444', num: 10 },
    { name: '555', num: 0 },
  ]
  Array.prototype.my_every = function(callback) {
    for(let i = 0 ; i < this.length; i++) {
     if(! callback(this[i],i,this) ){
       return false
     }
    }
    return true
  }

  const newSinger = singer.my_every((item, index, arr) => {
    return item.num > 10  // singer[4].num < 10
  })
  console.log(newSinger); //false 

2.2 Array.some()

1.some()原理

some() 方法也接受一个回调函数作为参数,与every()相反,它是检查数组中的任何元素是否通过我们的限制条件,some() 方法对数组中存在的每个元素执行一次函数:

  • 如果找到函数返回真值的数组元素,some() 返回ture,并且不检查剩余的元素
  • 否则返回 false
  • some() 不改变原始数组。

2.some()方法的手写实现

const singer = [
    { name: '111', num: 20 },
    { name: '222', num: 25 },
    { name: '333', num: 19 },
    { name: '444', num: 10 },
    { name: '555', num: 0 },
  ]
  Array.prototype.my_some = function(callback) {
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i], i, this)) {
        return true
      }
    }
    return false
  }
  const newSinger = singer.my_some((item, index, arr) => {
    return item.num > 20  // singer[1].num > 20
  })
  console.log(newSinger); // true

2.3 Array.map()

1.map()原理

map()方法接收三个参数,分别为 item(值)、index(下标)、arr(原数组),它使用为每个数组元素调用函数的结果创建新数组, 且函数一定要有return,并不会改变原始数组。

2.map()方法的手写实现

const singer = [
    { name: '111', num: 20 },
    { name: '222', num: 25 },
    { name: '333', num: 19 },
    { name: '444', num: 10 },
    { name: '555', num: 0 },
  ]
  Array.prototype.my_map = function(callback) {
    const res = []
    for (let i = 0; i < this.length; i++) {
      res.push(callback(this[i], i, this))
    }
    return res
  }

  const newSinger = singer.my_map((item, index, arr) => {
    return item.num > 20
  })
  console.log(newSinger); // [ false, true, false, false, false ]

2.4 Array.reduce()

1.reduce()原理

reduce() 方法接受两个参数,第一个参数是一个回调函数,这个回调函数接受四个参数,分别为pre(上次函数的执行结果)、next(值)、index(下标)、arr(原数组),第二个参数为第一次执行回调函数时pre的初始值。

如果我们没有给第二个参数,则pre为数组的第一个值,next为数组的第二个值。reduce() 方法也不会改变原始数组。一般我们会使用reduce方法实现数组的累加。

2.reduce()方法的手写实现

const singer = [
    { name: '111', num: 20 },
    { name: '222', num: 25 },
    { name: '333', num: 19 },
    { name: '444', num: 10 },
    { name: '555', num: 1 },
  ]
  Array.prototype.my_reduce = function(callback, ...args) {
    let pre, start = 0
    if (args.length) {
      pre = args[0]
    } else {
      pre = this[0]
      start = 1
    }
    for (let i = start; i < this.length; i++) {
      pre = callback(pre, this[i], i, this)
    }
    return pre
  }
  const newSinger = singer.my_reduce((pre, next, index, arr) => {
    console.log(pre);//0 20 45 64 74
    return pre + next.num
  }, 0)

  console.log(newSinger); // 75

2.5 Array.filter()

1.filter()原理

filter() 方法接收三个参数,分别为itemindexarr,他创建了一个新的数组,其中填充了所有满足我们的限制条件的元素。filter()也不会改变原始数组。

2.filter()方法的手写实现

const singer = [
    { name: '111', num: 20 },
    { name: '222', num: 25 },
    { name: '333', num: 19 },
    { name: '444', num: 10 },
    { name: '555', num: 0 },
  ]
  Array.prototype.my_filter = function(callback) {
    const res = []
    for (let i = 0; i < this.length; i++) {
      callback(this[i], i, this) && res.push(this[i])
    }
    return res
  }
  const newSinger = singer.filter((item, index, arr) => {
    return item.num > 15
  })
  console.log(newSinger); 
  // [
  //   { name: '111', num: 20 },
  //   { name: '222', num: 25 },
  //   { name: '333', num: 19 }
  // ]

2.6 Array.forEach()

1.forEach()原理

forEach() 方法一个回调函数作为参数,forEach()方法按顺序为数组中的每个元素调用一次函数。

2.forEach()方法的手写实现

const singer = [
  { name: '111', num: 20 },
  { name: '222', num: 25 },
  { name: '333', num: 19 },
  { name: '444', num: 10 },
  { name: '555', num: 0 },
]
  Array.prototype.my_forEach = function(callback) {
    for (let i = 0; i < this.length; i++) {
      callback(this[i], i, this)
    }
  }
  singer.my_forEach((item, index, arr) => {
    console.log(item, index);
  })
// { name: '111', num: 20 } 0
// { name: '222', num: 25 } 1
// { name: '333', num: 19 } 2
// { name: '444', num: 10 } 3
// { name: '555', num: 0 } 4

3、查找属性的API方法

3.1 Array.find()

1.find()原理

find() 方法接收三个参数,分别为 val(你要查找元素)、start(开始查找的位置)、end(结束的查找的位置),它返回数组中第一个通过限制的的元素的值。find() 方法对数组中存在的每个元素执行一次函数:

  • 如果找到函数返回 true 值的数组元素,则 find() 返回该数组元素的值(并且不检查剩余值)
  • 否则返回 undefined
  • find() 不会改变原始数组。

2.find()方法的手写实现

const singer = [
  { name: '111', num: 20 },
  { name: '222', num: 25 },
  { name: '333', num: 19 },
  { name: '444', num: 10 },
  { name: '555', num: 0 },
]
  Array.prototype.my_find = function(callback) {
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i], i, this)) {
        return this[i]
      }
    }
    return undefined
  }
  const res = singer.my_find((item, index, arr) => {
    return item.num > 20
  })

  console.log(res); // { name: '222', num: 25 }

3.2 Array.findIndex()

1.findIndex()原理

findIndex() 方法与find()方法原理相同,如果找到了返回对应下标值,否则返回-1

2.findIndex()方法的手写实现

const singer = [
    { name: '111', num: 20 },
    { name: '222', num: 25 },
    { name: '333', num: 19 },
    { name: '444', num: 10 },
    { name: '555', num: 0 },
  ]
  Array.prototype.my_findIndex = function(callback) {
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i], i, this)) {
        return i
      }
    }
    return -1
  }
  const res = singer.findIndex((item, index, arr) => {
    return item.num > 30
  })
  console.log(res); // -1

4、其他的好用API方法

4.1 Array.include()

1.includes()原理

includes() 方法接收两个参数,第一个参数为数组是否包含的元素,第二个参数为从数组某个位置开始向后查找,如果第二个个参数为负数,则从负数加上数组长度的位置开始。如果数组包含元素,则此方法返回 true,否则返回 false。且该方法区分大小写。

使用includes()方法时,我们要考虑当我们第一个参数为NaN时,由于NaN != NaN,所以在我们手写实现时,我们将这种情况考虑进去

2.includes()方法的手写实现

let obj = { name: '111', num: 25 }
const singer = [
  { name: '222', num: 20 },
  obj,
  { name: '333', num: 19 },
  { name: '444', num: 10 },
  { name: '555', num: 0 },
]
const nums = [1, 2, 3, 4, 5, NaN]
Array.prototype.my_includes = function(val, start = 0) {
  if (start < 0) start = this.length + start
  const isNaN = Number.isNaN(val)
  for (let i = start; i < this.length; i++) {
    if (this[i] === val || (isNaN && Number.isNaN(this[i]))) { // NaN === NaN
      return true
    }
  }
  return false
}
const res = nums.my_includes(NaN, 0)
console.log(res); // true

4.2 Array.join()

1.join()原理

join() 方法将数组作为字符串返回。元素将由指定的分隔符分隔。默认分隔符是逗号 ,join() 方法不会改变原始数组。

2.join()方法的手写实现

const arr = [1, 2, 3, 4, 'hello']
Array.prototype.my_join = function(s = ',') {
  let str = ''
  for (let i = 0; i < this.length; i++) {
    if (i === this.length - 1) {
      str += this[i]
    } else {
      str += this[i] + s
    }
    // str += i === this.length - 1 ? this[i] : this[i] + s // 简洁版
  }
  return str
}
const str = arr.my_join()
console.log(str);

4.3 Array.flat()

1.flat()原理

flat()方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。其接收一个参数默认值为1,表示需要深度遍历几次,超出数组维度则指挥遍历数组维度-1次,使其成为以为数组

2.flat()方法的手写实现

const arr = [ 1, 2, [3, 4, [5]], 6, {a: 7} ]
Array.prototype.myflat = function(num = 1) {
  let newArr = this
  let i = 0
  while (newArr.some(item => Array.isArray(item)) && i < num) {
    newArr = [].concat(...newArr) 
    i++
  }
  return newArr
}

const newArr = arr.myflat(4)

console.log(newArr); // [ 1, 2, 3, 4, 5, 6, { a: 7 } ]

4.4 Array.splice()

1.splice()原理

splice()方法可以接收任意个参数,它的第一个参数为切割数组的起始位置(start),第二个参数为我们需要切割的长度(length),而后面的参数则为我们需要添加到原数组上的值。splice会改变原来的数组。在我们手写splice()方法时,我们要注意以下几点:

  1. splice方法是先切割数组,再向其添加我们的想要向原数组的值
  2. splice方法会改变原数组,同时也会返回出一个新数组
  3. 当我们传进来的第一个参数为复数时,我们需要从(start+数组长度)的位置开始切割
  4. 当我们切割的长度大于原数组长度的最大值时,我们要改变新数组的长度,去除新数组后面的undefined

2.splice()方法的手写实现

const singer = [
  { name: '111', num: 20 },
  { name: '222', num: 25 },
  { name: '333', num: 19 },
  { name: '444', num: 10 },
  { name: '555', num: 0 },
]

Array.prototype.my_splice = function (start, length, ...args) {
  const result = [], targetArr = []; // 新数组 原数组
   // 判断第一个参数是否小于零
  if (start < 0) { 
    start = this.length + start
  }
// 生成新数组同时将原数组剩余的值和要添加的值给到 targetArr
  for (let i = 0; i < this.length; i++) {
    if (i === start) {
      for (let j = start; j < start + length; j++) {
        result.push(this[j]);
        i = j;
      }
      targetArr.push(...args);
      if (!length) { targetArr.push(this[i]); }
    } else {
      targetArr.push(this[i]);
    }
  }
// 给原数组赋值
  for (let i = 0; i < targetArr.length; i++) {
    this[i] = targetArr[i]
  }
  // 去除新数组上的undefined
  if (start + length > this.length) {
    if (start === 0) {
      result.length = this.length
    } else {
      result.length = this.length - 1 - start
    }
  }
  this.length = targetArr.length
  return result;

}
const arr = singer.my_splice(-2, 10, 'hello', 'world', '123')
console.log(singer);
// [
//   { name: '111', num: 20 },
//   { name: '222', num: 25 },
//   { name: '333', num: 19 },
//   'hello',
//   'world',
//   'laoli'
// ]
console.log(arr);
// [ { name: '444', num: 10 }, { name: '555', num: 0 } ]

4.5 Array.fill()

1.fill()原理

fill() 方法接受三个参数,分别是分别为 val(你要填充元素)、start(开始查找的位置)、end(结束的查找的位置),它是向数组中填充指定的元素。可以指定开始和结束填充的位置。如果未指定,则将填充所有元素。fill() 会覆盖原数组。要注意的一点是:当我们填充一个引用类型的数据时,当我们改变了该引用类型的值时,数组的值也会改变

2.fill()方法的手写实现

let obj = { name: '666', num: 12}
let singer = [
  { name: '111', num: 20 },
  { name: '222', num: 25 },
  { name: '333', num: 19 },
  { name: '444', num: 10 },
  { name: '555', num: 0 },
]
  Array.prototype.my_fill = function(val, start = 0, end) {
    end = end || this.length
    for (let i = start; i < end; i++) {
      this[i] = val
    }
    return this
  }
  singer.my_fill(obj, 0, 1)
  console.log(singer[0]); //{ name: '666', num: 12 }

  obj.num = 13
  console.log(singer[0]); // { name: '666', num: 12 }

5、结语

学习阶段,欢迎指教,码字不易,点个赞呗。