关于前端:建议收藏徒手实现24数组方法谁说你只是会用数组

28次阅读

共计 17658 个字符,预计需要花费 45 分钟才能阅读完成。

前言

数组是咱们日常工作中用的最频繁的一类数据结构,能帮忙咱们解决许多问题,而其自身也蕴含靠近 33 个之多的办法,做了一个脑图分类如下,纯熟应用数组的你,是否想晓得他们外部的实现原理呢?

这篇文章会和你一起探索 24+ 原生数组办法的外部实现,置信你看完肯定会有属于本人不一样的播种。

遍历类

1. forEach

根本应用

forEach一个日常用的十分多的遍历函数,你肯定相熟到不能再相熟啦!这里咱们着重看一些比拟重要且容易疏忽的点。mdn

  1. 该办法对数组的每个元素执行一次给定的函数,返回值是undefiend
  2. 该办法按升序为数组中 含有效值 的每一项执行一次 callback 函数,未初始化的项将被跳过(例如在稠密数组上)。
  3. 如果曾经存在的值被扭转,则传递给 callback 的值是 forEach() 遍历到他们那一刻的值。
  4. 已删除的项不会被遍历到

举个小例子



let demoArr = [1, 2, 3, 4, , 5]

demoArr.forEach((it, i) => {if (i === 1) {
    // 后增加进去的不会被拜访到
    demoArr.push(5)
  } else if (i === 2) {
    // 4 将不会被拜访到,而 4 - 4 会被拜访到
    demoArr.splice(3, 1, '4-4')
  }

  console.log(it)
})

/*
 1
 2
 3
 4-4
 5
*/

代码实现

点击查看源码实现

Array.prototype.forEach2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  const length = this.length
  let i = 0

  while (i < length) {
    // 被删除的,新增的元素索引 i 不在数组内,所以不会被拜访到
    if (i in this) {callback.call(thisCtx, this[ i], i, this)
    }

    i++
  }
}

同样用方才的例子,改下后的输入是一样的

测试一把


// 测试
let demoArr = [1, 2, 3, 4, , 5]

demoArr.forEach2((it, i) => {if (i === 1) {
    // 后增加进去的不会被拜访到
    demoArr.push(5)
  } else if (i === 2) {
    // 4 将不会被拜访到,相仿 4 - 4 会被拜访到
    demoArr.splice(3, 1, '4-4')
  }

  console.log(it)
})

/*
 1
 2
 3
 4-4
 5
*/

2. map

根本应用

map 办法创立一个新数组,其后果是该数组中的每个元素是调用一次提供的函数后的返回值。mdn

留神点

  1. callback 函数只会在有值的索引上被调用
  2. 素来没被赋过值或者应用 delete 删除的索引则不会被调用。
// 留神索引为 2 的地位没有赋值
let arr = [1, 2, ,4, 5]

// 删除索引 3
delete arr[3]

console.log(arr.map((it) => it * it))
// [1, 4, 25]

代码实现

点击查看源码实现

Array.prototype.map2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  const length = this.length
  let i = 0
  // map 返回值是一个新的数组
  let newArray = []

  while (i < length) {
    // 被删除的,未初始化的都不会被遍历到
    if (i in this) {newArray.push(callback.call(thisCtx, this[ i], i, this))
    }

    i++
  }
  // 返回新的数组
  return newArray
}

测试一把

let arr = [0, 1, 2, 3, 4,, 5]

let arr2 = arr.map2(function (it, i, array) {console.log(it, i, array, this)
  return it * it
}, {name: '前端胖头鱼'})

console.log(arr2)

3. every

根本应用

every 办法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。mdn

留神点

  1. 若收到一个空数组,此办法在所有状况下都会返回 true
  2. callback 只会为那些曾经被赋值的索引调用
  3. 不会为那些被删除或从未被赋值的索引调用
// 举例
let emptyArr = []
// 空数组间接返回 true
console.log(emptyArr.every((it) => it > 0)) // true
// 有未被赋值的
let arr = [0, 1, 2, 3, 4,, 5, -1]
// 删除元素
delete arr[7]

console.log(arr.every((it) => it >= 0)) // true

代码实现

点击查看源码实现


Array.prototype.every2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  const length = this.length
  let i = 0
  // 空函数不会走进循环
  while (i < length) {
    // 只有有一个值不合乎 callback 预期就返回 false
    if (i in this && !callback.call(thisCtx, this[ i], i, this)) {return false}

    i++
  }

  return true
}

测试一把

还是拿例子做测试


let emptyArr = []

console.log(emptyArr.every2((it) => it > 0)) // true

let arr = [0, 1, 2, 3, 4,, 5, -1]

delete arr[7]

console.log(arr.every2((it) => it >= 0)) // true

4. some

根本应用

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

留神点

  1. callback 只会在那些”有值“的索引上被调用,不会在那些被删除或从来未被赋值的索引上调用。

举个例子


let emptyArr = []
// 空数组间接返回 false
console.log(emptyArr.some((it) => it > 0)) // false
let arr = [0, 1, 2, 3, 4,, 5, -1]
// 还没有遍历前把 - 1 删除了,惟一小于 0 的值不存在了,即返回 false
delete arr[7]

console.log(arr.some((it) => it < 0)) // false

代码实现

点击查看源码实现

Array.prototype.some2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  const length = this.length
  let i = 0

  while (i < length) {
    // 只有有一个元素合乎 callback 条件,就返回 true
    if (i in this && callback.call(thisCtx, this[ i], i, this)) {return true}

    i++
  }

  return false
}

测试一把


let emptyArr = []
// 空数组间接返回 true
console.log(emptyArr.some2((it) => it > 0)) // false
let arr = [0, 1, 2, 3, 4,, 5, -1]

delete arr[7]

console.log(arr.some2((it) => it < 0)) // false
console.log(arr.some2((it) => it > 0)) // true

5. filter

根本应用

filter 办法创立一个新数组, 其蕴含通过所提供函数测试的所有元素。mdn

留神点

  1. filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或等价于 true 的值的元素创立一个新数组。
  2. callback 只会在曾经赋值的索引上被调用,对于那些曾经被删除或者从未被赋值的索引不会被调用。
  3. 那些没有通过 callback 测试的元素会被跳过,不会被蕴含在新数组中。
// 索引为 5 的地位,没有初始化值,不会被遍历
let arr = [0, 1, 2, -3, 4,, 5]
// 删除掉最初一个元素
delete arr[6]
// 过滤出大于 0 的值
let filterArr = arr.filter((it) => it > 0)

console.log(filterArr) // [1, 2, 4]

代码实现

点击查看源码实现

Array.prototype.filter2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  const length = this.length
  let newArray = []
  let i = 0

  while (i < length) {if (i in this && callback.call(thisCtx, this[ i], i, this)) {newArray.push(this[ i])
    }
    i++
  }

  return newArray
}

测试


// 索引为 5 的地位,没有初始化值,不会被遍历
let arr = [0, 1, 2, -3, 4,, 5]
// 删除掉最初一个元素
delete arr[6]
// 过滤出大于 0 的值
let filterArr = arr.filter2((it) => it > 0)

console.log(filterArr) // [1, 2, 4]

6. reduce

根本应用

reduce 办法对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其后果汇总为单个返回值 mdn

这个函数略微简单一些,咱们用一个例子来看一下他是怎么用的。


const sum = [1, 2, 3, 4].reduce((prev, cur) => {return prev + cur;})

console.log(sum) // 10

// 初始设置
prev = initialValue = 1, cur = 2

// 第一次迭代
prev = (1 + 2) =  3, cur = 3

// 第二次迭代
prev = (3 + 3) =  6, cur = 4

// 第三次迭代
prev = (6 + 4) =  10, cur = undefined (退出)

代码实现

点击查看源码实现

Array.prototype.reduce2 = function (callback, initValue) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  let pre = initValue
  let i = 0
  const length = this.length
  // 当没有传递初始值时,取第一个作为初始值  
  if (typeof pre === 'undefined') {pre = this[0]
    i = 1
  }

  while (i < length) {if (i in this) {pre = callback(pre, this[ i], i, this)
    }
    i++
  }

  return pre
}

测试一把

const sum = [1, 2, 3, 4].reduce2((prev, cur) => {return prev + cur;})

console.log(sum) // 10

7. reduceRight

根本应用

reduceRight 办法对数组中的每个元素执行一个由您提供的 reducer 函数(降序执行),将其后果汇总为单个返回值 mdn

和 reduce 很相似,惟一不同的是 reduceRight 从右往左遍历

const sum = [1, 2, 3, 4].reduce((prev, cur) => {console.log(cur)
  return prev + cur;
})

// 2 1
// 3 2
// 4 3

console.log(sum) // 10

const sum2 = [1, 2, 3, 4].reduceRight((prev, cur) => {console.log(cur)
  return prev + cur;
})
// 3 2 
// 2 1
// 1 0

console.log(sum2) // 10

代码实现

点击查看源码实现

Array.prototype.reduceRight2 = function (callback, initValue) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  let pre = initValue
  const length = this.length
  // 从最初一个元素开始遍历
  let i = length - 1
  // 如果没有传递初始值,则取最初一个作为初始值
  if (typeof pre === 'undefined') {pre = this[i]
    i--
  }

  while (i >= 0) {if (i in this) {pre = callback(pre, this[ i], i, this)
    }
    i--
  }

  return pre
}

测试一把

const sum = [1, 2, 3, 4].reduceRight2((prev, cur) => {console.log(cur)
  return prev + cur;
})

// 3 2
// 2 1
// 1 0

console.log(sum) // 10

查找类

8. find

根本应用

find 办法返回数组中满足测试函数的第一个元素的值。否则返回 undefined, mdn

留神点

  1. find办法对数组中的每一项元素执行一次 callback 函数,直至有一个 callback 返回 true
  2. 当找到了这样一个元素后,该办法会立刻返回这个元素的值,否则返回 undefined
  3. callback 函数会为数组中的每个索引调用即从 到 length - 1,而不仅仅是那些被赋值的索引。(这个点是和后面几个函数不一样的中央)
let arr = [0, 1, 2, 3, 4,, 5]

let index = arr.find((it) =>  {return it > 3}, {name: '前端胖头鱼'})

console.log(index) // 4

代码实现

点击查看源码实现


Array.prototype.find2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  const length = this.length
  let i = 0

  while (i < length) {const value = this[ i]
    // 只有有一个元素合乎 callback 回调函数的逻辑,就返回元素 value
    if (callback.call(thisCtx, value, i, this)) {return value}

    i++
  }
  // 否则返回 undefined  
  return undefined
}

测试一把


let arr = [0, 1, 2, 3, 4,, 5]

let index = arr.find2(function (it, i, array) {console.log(it, i, array, this)
  return it > 3
}, {name: '前端胖头鱼'})

console.log(index) // 4

9. findIndex

根本应用

findIndex办法返回数组中满足提供的测试函数的第一个元素的 索引。若没有找到对应元素则返回 -1。mdn

和 find 函数不同的中央在于,findIndex 是返回 索引而非值, 留神点也和 find 根本一样

  1. findIndex办法对数组中的每个数组索引 0 ~ length-1(包含)执行一次callback 函数,直到找到一个 callback 函数返回 true 的值。
  2. 如果找到这样的元素,findIndex会立刻返回该元素的索引。如果回调从不返回真值,或者数组的 length 为 0,则 findIndex 返回 -1
  3. 与某些其余数组办法(如 Array#some)不同,在稠密数组中,即便对于数组中不存在的条目标索引也会调用回调函数
let arr = [0, 1, 2, 3, 4,, 5]

let index = arr.findIndex((it, i, array) => {return it > 2})

console.log(index) // 3

代码实现

点击查看源码实现

Array.prototype.findIndex2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }

  const length = this.length
  let i = 0

  while (i < length) {
    // 合乎 callback 逻辑的间接返回索引 i
    if (callback.call(thisCtx, this[ i], i, this)) {return i}

    i++
  }
  // 否则返回 -1  
  return -1
}

测试一把

let arr = [0, 1, 2, 3, 4,, 5]

let index = arr.findIndex2(function (it, i, array) {console.log(it, i, array, this)
  return it > 2
}, {name: '前端胖头鱼'})

console.log(index) // 3

10. indexOf

根本应用

indexOf办法返回在数组中能够找到一个给定元素的第一个索引,如果不存在,则返回 -1。mdn

arr.indexOf(searchElement[, fromIndex])

留神点

  1. 如果开始查找的索引值大于或等于数组长度,意味着不会在数组里查找,返回 -1
  2. 如果参数中提供的索引值是一个 负值,则将其作为数组开端的一个对消,即 - 1 示意从最初一个元素开始查找,- 2 示意从倒数第二个元素开始查找,以此类推
  3. 如果参数中提供的索引值是一个负值,并不扭转其查找程序,查找程序依然是从前向后查问数组
  4. 如果对消后的索引值仍小于 0,则整个数组都将会被查问。其默认值为 0.
const array = [2, 5, 9]

console.log(array.indexOf(2))      // 0
console.log(array.indexOf(7))      // -1
console.log(array.indexOf(9, 2))   // 2
console.log(array.indexOf(2, -1))  // -1
console.log(array.indexOf(2, -3))  // 0

代码实现

点击查看源码实现

有了下面的留神点和根本你应用,聪慧的你必定一眼就晓得怎么写啦

Array.prototype.indexOf2 = function (targetEle, fromIndex) {
  const length = this.length

  fromIndex = +fromIndex || 0

  // 数组为空或者从大于等于数组长度的中央开始检索,都间接是 -1
  if (length === 0 || fromIndex >= length) {return -1}
  /*
    1. 从 fromIndex 开始搜寻元素
    2. fromIndex 大于 0 时候间接取即可
    3. 小于 0 先用长度减去 fromIndex 的绝对值,如果还是小于 0,就间接取 0 即可
  */
  let i = Math.max(fromIndex >= 0 ? fromIndex : length - Math.abs(fromIndex), 0)

  while (i < length) {
    // 在数组内的元素并且和 targetEle 强等
    if (i in this && targetEle === this[ i]) {return i}

    i++
  }

  return -1
}

测试一把


const array = [2, 5, 9]

console.log(array.indexOf2(2))      // 0
console.log(array.indexOf2(7))      // -1
console.log(array.indexOf2(9, 2))   // 2
console.log(array.indexOf2(2, -1))  // -1
console.log(array.indexOf2(2, -3))  // 0

11. lastIndexOf

根本应用

lastIndexOf 办法返回指定元素在数组中的最初一个的索引,如果不存在则返回 -1。mdn

arr.lastIndexOf(searchElement[, fromIndex])

留神点

  1. arr.length - 1 地位开始逆向查找。
  2. 如果 fromIndex 大于或等于数组的长度,则整个数组会被查找。
  3. 如果 fromIndex 为负值,将其视为从数组开端向前的偏移。即便该值为负,数组依然会被从后向前查找。
  4. 如果 fromIndex 值为负时,其绝对值大于数组长度,则办法返回 -1,即数组不会被查找。
let array = [2, 5, 9, 2]

console.log(array.lastIndexOf(2)) // 3
console.log(array.lastIndexOf(7)) // -1
console.log(array.lastIndexOf(2, 3)) // 3
console.log(array.lastIndexOf(2, 2)) // 0
console.log(array.lastIndexOf(2, -2)) // 0
console.log(array.lastIndexOf(2, -1)) // 3

代码实现

点击查看源码实现

Array.prototype.lastIndexOf2 = function (targetEle, fromIndex) {
  const length = this.length

  fromIndex = typeof fromIndex === 'undefined' ? length - 1 : fromIndex
  // 数组为空,以及该值为负时且绝对值大于数组长度,则办法返回 -1,即数组不会被查找。if (length === 0 || fromIndex < 0 && Math.abs(fromIndex) >= length) {return -1}

  let i

  if (fromIndex >= 0) {
    // 如果 `fromIndex` 大于或等于数组的长度,则整个数组会被查找。// 也就是当大于数组 length - 1 时,会取 length - 1
    i = Math.min(fromIndex, length - 1)
  } else {i = length - Math.abs(fromIndex)
  }

  while (i >= 0) {
    // 等于 targetEle 时返回索引
    if (i in this && targetEle === this[ i]) {return i}
    // 逆向遍历
    i--
  }
  // 没找到返回 -1
  return -1
}

测试一把


let array = [2, 5, 9, 2]

console.log(array.lastIndexOf2(2)) // 3
console.log(array.lastIndexOf2(7)) // -1
console.log(array.lastIndexOf2(2, 3)) // 3
console.log(array.lastIndexOf2(2, 2)) // 0
console.log(array.lastIndexOf2(2, -2)) // 0
console.log(array.lastIndexOf2(2, -1)) // 3

12. includes

根本应用

includes 办法用来判断一个数组是否蕴含一个指定的值, 如果蕴含则返回 true,否则返回 false。mdn

arr.includes(valueToFind[, fromIndex])

留神点

  1. fromIndex 索引处开始查找 valueToFind
  2. 如果为负值,则按升序从 array.length + fromIndex 的索引开始搜
  3. 数组中存在 NaN 的话,[..., NaN].includes(NaN)为 true

console.log([1, 2, 3].includes(2))     // true
console.log([1, 2, 3].includes(4))     // false
console.log([1, 2, 3].includes(3, 3))  // false
console.log([1, 2, 3].includes(3, -1)) // true
console.log([1, 2, NaN].includes(NaN)) // true

代码实现

点击查看源码实现

Array.prototype.includes2 = function (targetEle, fromIndex) {
  const length = this.length

  fromIndex = +fromIndex || 0

  // 数组为空或者从大于等于数组长度的中央开始检索,都间接是 -1
  if (length === 0 || fromIndex >= length) {return false}
  /*
    1. 从 fromIndex 开始搜寻元素
    2. fromIndex 大于 0 时候间接取即可
    3. 小于 0 先用长度减去 fromIndex 的绝对值,如果还是小于 0,就间接取 0 即可
  */
  let i = Math.max(fromIndex >= 0 ? fromIndex : length - Math.abs(fromIndex), 0)

  while (i < length) {const value = this[ i]
    // 留神 NaN 状况
    if (targetEle === value || typeof targetEle === 'number' && typeof value === 'number' && isNaN(targetEle) && isNaN(value)) {return true}

    i++
  }

  return false
}

测试一把


console.log([1, 2, 3].includes2(2))     // true
console.log([1, 2, 3].includes2(4))     // false
console.log([1, 2, 3].includes2(3, 3))  // false
console.log([1, 2, 3].includes2(3, -1)) // true
console.log([1, 2, NaN].includes2(NaN)) // true

增删改类

13. push

根本应用

push 办法将一个或多个元素增加到数组的开端,并返回该数组的新长度。mdn

const animals = ['pigs', 'goats', 'sheep']
animals.push('cows')

console.log(animals, animals.length) 
// ["pigs", "goats", "sheep", "cows"], 4

animals.push('chickens', 'cats', 'dogs')

console.log(animals, animals.length) 

// ["pigs", "goats", "sheep", "cows", "chickens", "cats", "dogs"], 7

代码实现

点击查看源码实现


Array.prototype.push2 = function (...pushEles) {
  const pushEleLength = pushEles.length
  const length = this.length

  let i = 0
  
  while (i < pushEleLength) {this[ length + i] = pushEles[i]
    i++
  }

  return this.length
}

测试一把

const animals = ['pigs', 'goats', 'sheep']
animals.push2('cows')

console.log(animals, animals.length) 
// ["pigs", "goats", "sheep", "cows"], 4

animals.push2('chickens', 'cats', 'dogs')

console.log(animals, animals.length) 

// ["pigs", "goats", "sheep", "cows", "chickens", "cats", "dogs"], 7

14. pop

根本应用

pop办法从数组中删除最初一个元素,并返回该元素的值。此办法更改数组的长度。mdn

let arr = [1, 2]
let arr2 = []

console.log(arr.pop(), arr) // 2 [1]
console.log(arr2.pop(), arr2) // undefined []

代码实现和应用一样简略,只有把数组的最初一个元素返回,并且让数组长度减 1 即可

代码实现

点击查看源码实现

Array.prototype.pop2 = function () {
  const length = this.length
  // 空数组上 pop,间接返回 undefined
  if (length === 0) {return undefined}

  const delEle = this[length - 1]

  this.length = length - 1

  return delEle
}

测试一把

let arr = [1, 2]
let arr2 = []

console.log(arr.pop2(), arr) // 2 [1]
console.log(arr2.pop2(), arr2) // undefined []

15. unshift

根本应用

unshift  办法将一个或多个元素增加到数组的 结尾 ,并返回该数组的 新长度 (该 办法批改原有数组 )

留神点

  1. 如果传入多个参数,它们会被以块的模式插入到对象的开始地位,它们的程序和被作为参数传入时的程序统一。
  2. 传入多个参数调用一次 unshift,和传入一个参数调用屡次 unshift (例如,循环调用),它们将失去不同的后果。例如:
let arr = [4,5,6]
// 一次性插入
arr.unshift(1,2,3)

console.log(arr) // [1, 2, 3, 4, 5, 6]

let arr2 = [4,5,6]
// 插入屡次
arr2.unshift(1)
arr2.unshift(2)
arr2.unshift(3)

console.log(arr2); // [3, 2, 1, 4, 5, 6]

代码实现

点击查看源码实现

Array.prototype.unshift2 = function (...unshiftEles) {
  // 借助扩大符,将须要增加的元素以块的模式插入到数组后面
  let newArray = [...unshiftEles, ...this]
  let length = newArray.length
  
  let i = 0

  if (unshiftEles.length === 0) {return length}
  // 从新复制给数组
  while (i < length) {this[ i] = newArray[i]
    i++
  }
  
  return this.length
}

测试一把


let arr = [4,5,6]
// 一次性插入
arr.unshift2(1,2,3)

console.log(arr) // [1, 2, 3, 4, 5, 6]

let arr2 = [4,5,6]
// 插入屡次
arr2.unshift2(1)
arr2.unshift2(2)
arr2.unshift2(3)

console.log(arr2); // [3, 2, 1, 4, 5, 6]

16. shift

根本应用

shift 办法从数组中删除 第一个 元素,并返回该元素的值。mdn


let arr = [1, 2]

console.log(arr.shift(), arr) // 1 [2]
console.log(arr.shift(), arr) // 2 []

代码实现

点击查看源码实现

Array.prototype.shift2 = function () {
  const length = this.length
  const delValue = this[0]

  let i = 1

  while (i < length) {
    // 从第一个元素开始,前面的元素都往前挪动一位
    this[i - 1] = this[i]
    i++
  }
  // 设置好数组的长度
  this.length = length - 1
  // 返回删除的值
  return delValue
}

测试一把


let arr = [1, 2]

console.log(arr.shift2(), arr) // 1 [2]
console.log(arr.shift2(), arr) // 2 []

17. reverse

根本应用

reverse 办法将数组中元素的地位颠倒,并返回该数组。即数组的第一个元素会变成最初一个,数组的最初一个元素变成第一个。mdn

const arr = [1, 2, 3]

console.log(arr) // [1, 2, 3]

arr.reverse()

console.log(arr) // [3, 2, 1]

代码实现

点击查看源码实现

Array.prototype.reverse2 = function () {
  // 设置双指针,往两头聚拢
  let i = 0
  let j = this.length - 1

  while (i < j) {
    // 第一个和最初一个,第二个和倒数第二个进行地位调换
    [this[ i], this[j] ] = [this[ j], this[i] ]
    i++
    j--
  }

  return this
}

测试一把


const arr = [1, 2, 3]

console.log(arr) // [1, 2, 3]

arr.reverse2()

console.log(arr) // [3, 2, 1]

18. fill

根本应用

fill  办法用一个固定值填充一个数组中从起始索引到终止索引内的全副元素。不包含终止索引。mdn


const array1 = [1, 2, 3, 4];

console.log(array1.fill(0, 2, 4)) // [1, 2, 0, 0]


console.log(array1.fill(5, 1)) // [1, 5, 5, 5]

console.log(array1.fill(6)) // [6, 6, 6, 6]

代码实现

点击查看源码实现

Array.prototype.fill2 = function (value, start, end) {
  const length = this.length

  start = start >> 0
  // end 没填的话,默认是 length,否则取填写的 
  end = typeof end === 'undefined' ? length : end >> 0
  // start 最小取 0,最大取 length
  start = start >= 0 ? Math.min(start, length) : Math.max(start + length, 0)
  // end 最小取 0,最大取 length 
  end = end >= 0 ? Math.min(end, length) : Math.max(end + length, 0)
  // 填充指定范畴的索引为 value
  while (start < end) {this[ start] = value
    start++
  }
  // 返回被批改的数组
  return this
}

测试一把


const array1 = [1, 2, 3, 4];

console.log(array1.fill2(0, 2, 4)) // [1, 2, 0, 0]


console.log(array1.fill2(5, 1)) // [1, 5, 5, 5]

console.log(array1.fill2(6)) // [6, 6, 6, 6]

连贯、拼接

19. concat

根本应用

concat 办法用于合并两个或多个数组。此办法不会更改现有数组,而是返回一个新数组 mdn



let num1 = [[1]]
let num2 = [2, [3]]
let num3=[5,[6]]

let nums = num1.concat(num2) // [[1], 2, [3]]
let nums2 = num1.concat(4, num3) // [[1], 4, 5,[6]]

代码实现

点击查看源码实现

Array.prototype.concat2 = function (...concatEles) {
  const length = concatEles.length
  // 数组自身开展一层
  let newArray = [...this]
  let i = 0

  while (i < length) {const value = concatEles[ i]
    // 对数组元素开展一层 
    Array.isArray(value) ? newArray.push(...value) : newArray.push(value)
    i++
  }

  return newArray
}

测试一把


let num1 = [[1]]
let num2 = [2, [3]]
let num3=[5,[6]]

let nums = num1.concat2(num2) // [[1], 2, [3]]
let nums2 = num1.concat2(4, num3) // [[1], 4, 5,[6]]

20. join

根本应用

join 办法将一个数组的所有元素通过 字符标识 连接成一个字符串并返回这个字符串。如果数组只有一个我的项目,那么将返回该我的项目而不应用分隔符。

const elements = ['Fire', 'Air', 'Water']
const elements2 = ['Fire']

console.log(elements.join()) // Fire,Air,Water
console.log(elements.join('')) // FireAirWater
console.log(elements.join('-')) //  Fire-Air-Water
console.log(elements2.join('-')) // Fire

代码实现

点击查看源码实现

Array.prototype.join2 = function (format = ',') {
  const length = this.length
  // 保留最初一个元素,因为他不参加 format 连贯 
  let lastEle = this[length - 1]
  let string = ''

  if (length === 0) {return string}

  for (i = 0; i < length - 1; i++) {string += this[ i] + format
  }

  return string + lastEle
}

测试一把

const elements = ['Fire', 'Air', 'Water']
const elements2 = ['Fire']

console.log(elements.join2()) // Fire,Air,Water
console.log(elements.join2('')) // FireAirWater
console.log(elements.join2('-')) //  Fire-Air-Water
console.log(elements2.join2('-')) // Fire

静态方法

21. Array.isArray

根本应用

Array.isArray() 用于确定传递的值是否是一个 Array


Array.isArray([1, 2, 3]) // true

Array.isArray({foo: 123}) // false

Array.isArray("foobar") // false

Array.isArray(undefined) // false

代码实现

点击查看源码实现

这个非常简单,只须要一句话就能够

Array.isArray2 = function (ele) {return  Object.prototype.toString.call(ele) === '[object Array]';
}

测试一把


Array.isArray2([1, 2, 3]) // true

Array.isArray2({foo: 123}) // false

Array.isArray2("foobar") // false

Array.isArray2(undefined) // false

22. Array.of

根本应用

Array.of 办法创立一个具备可变数量参数的新数组实例,而不思考参数的数量或类型。

留神点

Array.of()  和 Array 构造函数之间的区别在于解决整数参数:

Array.of(7) 创立一个具备单个元素 7 的数组,而 Array(7) 创立一个长度为 7 的空数组(留神: 这是指一个有 7 个空位 (empty) 的数组,而不是由 7 个 undefined 组成的数组)

Array.of(7);       // [7]
Array.of(1, 2, 3); // [1, 2, 3]

Array(7);          // [, , , , , ,]
Array(1, 2, 3);    // [1, 2, 3]

代码实现

点击查看源码实现

实现思路就是把你穿进去的值,挨个赋值到以后数组即可

Array.of2 = function (...eles) {
  const length = eles.length
  let i = 0
  let newArray = []

  while (i < length) {newArray[ i] = eles[i]
    i++
  }

  return newArray
}

测试一把


Array.of2(7);       // [7]
Array.of2(1, 2, 3); // [1, 2, 3]

扁平类

23. flat

根本应用

flat()  办法会依照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。mdn

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat()) // [0, 1, 2, 3, 4] 默认会平铺开展一层


const arr2 = [0, 1, 2, [[[3, 4]]]]

console.log(arr2.flat(2)) // [0, 1, 2, [3, 4]] 指定开展两层

代码实现

点击查看源码实现


Array.prototype.flat2 = function (depth = 1) {const result = []
  const flat = (arr, depth) => {for (let item of arr) {
      // 当层数还未全副开展的时候,进行递归解决
      if (Array.isArray(item) && depth > 0) {flat(item, depth - 1)
      } else {
        // 去除空元素,增加非 undefined 元素
        item !== void 0 && result.push(item)
      }
    }
  }

  flat(this, depth)

  return result
}

测试一把


const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat2()) // [0, 1, 2, 3, 4]


const arr2 = [0, 1, 2, [[[3, 4]]]]

console.log(arr2.flat2(2)) // [0, 1, 2, [3, 4]] 

24. flatMap

根本应用

flatMap 办法首先应用映射函数映射每个元素,而后将后果压缩成一个新数组。它与 map 连着深度值为 1 的 flat 简直雷同。mdn


let arr = [1, 2, 3, 4]


arr.flatMap(x => [x * 2]) // [2, 4, 6, 8]

代码实现

点击查看源码实现


Array.prototype.flatMap2 = function (callback, thisCtx) {if (typeof callback !== 'function') {throw `${callback} is not a function`
  }
  // map 和 flat 具体实现能够看 map.js 和 flat.js
  return this.map(function (it, i, array) {return callback.call(thisCtx, it, i, array)
  }).flat(1)
}

测试

let arr = [1, 2, 3, 4]


arr.flatMap2(x => [x * 2]) // [2, 4, 6, 8]

结尾

国庆将至,祝大家节日快乐,浪浪浪七天乐。

文章中可能蕴含实现有问题或者不够充沛的状况,欢送大家在评论区指出,肯定快马加鞭地改过,拜谢。

篇幅起因,还有不少数组办法没有写在文章中,如果对大家有一些用途,后续还会出一篇splicekeysvalues… 等残余函数的原生实现。祝大家晚安啦 🌹

正文完
 0