数组方法手写原理
刀刀
3/20/2025
0 字
0 分钟
forEach
forEach
主要是做循环遍历操作,接收一个回调函数,该回调函数传递三个参数:
- 参数1:该遍历项的每一项数据(即
item
) - 参数2:该项的索引(即
index
) - 参数3:该数组
Array.prototype.my_foreach = function(callback) {
for(let i = 0; i < this.length; i++) {
callback(this[i], i, this)
}
}
其中,这里的 this
指向的是使用该方法的数组(如下方示例中的 daodao
数组),而 for
循环需要获取到数组的长度,因此用到了 this.lengh
,每一项的数据则是 this[i]
。
下面使用一下自己封装好的 my_foreach
方法:
daodao.my_foreach((item, index, arr) => {
console.log(item, index, arr)
})
打印结果如下所示:
拓展
使用过 forEach
的程序员都知道 return
无法终止 forEach
循环,现在知道其原理了,因为我们在使用的 forEach
里写的 return
是在回调函数 callback
中,return
只能跳出当前的循环,无法跳出整体的循环。
map
map
主要做某种操作,并返回一个新的数组,接收一个回调函数,该回调函数传递三个参数:
- 参数1:该遍历项的每一项数据(即
item
) - 参数2:该项的索引(即
index
) - 参数3:该数组
Array.prototype.my_map = function(callback) {
const arr = []
for(let i = 0; i < this.length; i++) {
arr.push(callback(this[i], i, this)) // 此时拿到的是数组的每一条数据
}
return arr
}
由于要返回一个新数组,因此需要在循环体外先定义一个空数组,每一次循环都往数组内 push
一次数据,最后再 return
返回。
const newdaodao = daodao.my_map((item, index) => {
return {
...item,
name: item.name + ',你好'
}
})
console.log(newdaodao);
最后运行结果如下所示:
[
{
"age": 20,
"name": "刀刀,你好"
},
{
"age": 21,
"name": "小刀,你好"
},
{
"age": 22,
"name": "杜一刀,你好"
}
]
filter
filter
每一遍历一次回调函数都会返回一个布尔值,如果该值为真才添加到数组中,为假则不添加,接收一个回调函数,该回调函数传递三个参数:
- 参数1:该遍历项的每一项数据(即
item
) - 参数2:该项的索引(即
index
) - 参数3:该数组
Array.prototype.my_filter = function(callback) {
const arr = []
for(let i = 0; i < this.length; i++) {
// callback(this[i], i, this) && arr.push(callback(this[i], i, this)) // 这么写的话最终返回的结果是:(2) [true, true].因为此时拿到的是一个布尔值
callback(this[i], i, this) && arr.push(this[i])
}
return arr
}
使用:
const newdaodao = daodao.my_filter((item, index) => item.name.length <= 2)
console.log(newdaodao);
结果返回:
[
{
"age": 20,
"name": "刀刀"
},
{
"age": 21,
"name": "小刀"
}
]
every
遍历所有项并判断是否匹配要求,如果都匹配返回 true,只要有一个不符合要求返回 false。
实现思路与 filter
类似,回调函数返回的是一个判断后的布尔值,遍历后判断其是否为真,只要有一项为假,就返回假。都不为假才返回真。
Array.prototype.my_every = function(callback) {
for(let i = 0; i < this.length; i++) {
// callback(this[i], i, this) // 这里拿到的是一个布尔值
if(!callback(this[i], i, this)) return false
}
return true
}
使用:
const newdaodao1 = daodao.my_every((item, index) => item.name.length <= 3)
const newdaodao2 = daodao.my_every((item, index) => item.name.length <= 2)
console.log(newdaodao1); // true
console.log(newdaodao2); // false
some
some
与 every
相反,some
只需要有一个匹配就能返回 true,只有在全部都不匹配的情况下才返回 false。因此只需要把上方 every
的代码稍微调整一下即可。
Array.prototype.my_some = function(callback) {
for(let i = 0; i < this.length; i++) {
// callback(this[i], i, this) // 这里拿到的是一个布尔值
if(callback(this[i], i, this)) return true
}
return false
}
find
find 主要用于寻找符合要求的第一项数组数据,如果遍历完数组都没有找到匹配数据,则返回 undefined。与 some、every、filter 一样,回调函数拿到的是一个判断布尔值,因此同样使用 if 来判断,如果符合要求则返回该项的数据。
Array.prototype.my_find = function(callback) {
for(let i = 0; i < this.length; i++) {
// callback(this[i], i, this) // 这里拿到的是一个布尔值
if(callback(this[i], i, this)) return this[i] // 返回这一项
}
return undefined
}
findIndex
与 find 相同,判断逻辑上一样,区别是 findIndex 返回的是索引,因此修改一下返回的值为索引即可。如果都找不到数据,则返回 -1 。
Array.prototype.my_find = function(callback) {
for(let i = 0; i < this.length; i++) {
// callback(this[i], i, this) // 这里拿到的是一个布尔值
if(callback(this[i], i, this)) return i // 返回这一项的索引值i
}
return -1
}
reduce
Array.prototype.my_reduce = function(callback, ...args) {
let start = 0, pre
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
}
reduce
函数接收两个参数,一个是回调函数 callback
,另一个是起始值 args
。其中,回调函数执行后又返回四个参数:
- 参数1:当前计算累加的值。判断用户是否传递了初始值,传递了就赋值给参数1,没传递则获取数组的第一项数据(在循环体外部做判断)
- 参数2:遍历的第 i 项数据
- 参数3:索引
- 参数4:整个数组
注意,如果使用者没有传初始值,则参数1获取的是数组第一项,此时累加则是从数组第二项开始循环遍历,因此 for 循环中 i 的起始值需要动态判断(上方示例代码中 strat
的作用正是如此)。
最后把累加的值返回即可。
fill
Array.prototype.my_fill = function(init, start = 0, end) {
end = end < 0 ? this.length + end : end
for(let i = 0; i < end; i++) {
this[i] = value
}
return this
}
includes
Array.prototype.my_includes = function(value, start) {
start = start < 0 ? this.length + start : start
for(let i = 0; i < this.length; i++) {
// 判断当前的值是否等于要比较的值,相等返回true
if(this[i] === value) return true
}
return false
}
join
Array.prototype.my_join = function(s = ',') {
let str = ''
for(let i = 0; i < this.length; i++) {
strr = i === 0 ? `${str}${this[i]}` : `${str}${s}${this[i]}` // 判断当前是否为第一项,第一项则不添加分隔符
}
return str
}
flat
Array.prototype.my_flat = function(num = Infinity) {
let arr = this
let i = 0
while(arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr)
i++
if(i >= num) break
}
return arr
}