手写数组方法之用于遍历的方法

forEach

用法

可以遍历数组的每个元素,执行特定的操作:

  • 接受两个参数,一个回调函数和一个缺省为 null 的 thisArg。会遍历数组中所有元素,对每个元素执行该函数;
  • 不会新建数组也不会修改原数组,总是返回 undefined(即使明确指定了返回值)
  • 遍历范围在执行回调前就确定了,遍历过程中 push 会修改原数组,但是不会影响遍历范围
  • 遍历过程中可以提前修改后面才会遍历到的元素
  • 会直接跳过 empty 元素
// res 为 undefined
const res = ['a','b','c'].forEach(function(item,index,arr){
    console.log(`${index}-${item}`,arr,this)
    return 123
},{})                                                        
// 遍历过程中即使push了新元素,也仍然按照原数组长度进行遍历。打印 1,2
[1,2].forEach((item,index,arr) => {
    arr.push(3,4,5)
    console.log(item)
})
// 遍历过程中可以提前修改未遍历元素。打印 1,100
[1,2].forEach((item,index,arr) => {
    arr[1] = '100'
    console.log(item)
})
// 遍历过程中可以 return,但只是结束当前这次遍历,无法跳出整个 forEach。打印 2
[1,2].forEach((item,index,arr) => {
    if(index == 0) return
    console.log(item)
})
// 遍历过程中会自动跳过 empty 元素(null 和 undefined 不会跳过)。打印 1,2,4
[1,2,,4].forEach((item,index,arr) => {    
    console.log(item)
})

实现

Array.prototype.myforEach = function (fn,thisArg = null){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this    
    for(let i = 0,len = arr.length;i < len;i++){
        // 如果不是 empty 元素
        if(i in arr){
            fn.call(thisArg,arr[i],i,arr)
        }
    }
}

map

用法

将原数组的每个元素映射为执行回调函数之后的返回值

  • 基本实现和 forEach 差不多,也是会跳过 empty 元素
  • forEach 的遍历范围一开始就确定好,所以需要先保存最初数组长度;同理,map 最终返回的新数组长度也是一开始就与原数组长度绑定好了,声明新数组的时候需要指定这个长度
// 返回新数组 [2,4,6,8]
[1,2,3,4].map((item,index) => item * 2)

实现

Array.prototype.myMap = function(fn,thisArg = null){
    if(typeof fn != 'function'){
        throw TypeError(`${fn} is not a function`)
    }
    let arr = this
    // 这里不要使用 let newArr = [],否则修改原数组长度时会影响新数组长度
    let newArr = new Array(arr.length)
    for(let i = 0,len = arr.length;i < len;i++){
        if(i in arr){
            const res = fn.call(thisArg,arr[i],i,arr)
            newArr[i] = res
        }
    }
    return newArr
}

flatMap

用法

flatMap 相当于是 map 和 flat(1) 的结合。它会给某个数组调用 map 方法,如果得到了一个多维数组,则会对该数组进行一次降维

PS:注意这个方法不会改变原数组。

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

arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]

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

实现

我们可以在每次执行 flatMap 的回调并返回一个新结果时,判断该结果是不是数组,如果是则取出数组中的每个元素放入最终返回的新数组中。

Array.prototype.myFlatMap = function(fn,thisArg = null){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this
    let newArr = new Array(arr.length)
    let k = 0
    for(let i = 0;i < arr.length;i++){
        if(i in arr){
            const res = fn.call(thisArg,arr[i],i,arr)
            if(Array.isArray(res)){
                for(let el of res){
                    newArr[k++] = el
                }
            } else {
                newArr[k++] = res
            }
        }
    }   
    return newArr
}

find

用法

find 返回数组中第一个符合条件的元素或者 undefined:

  • 通过indexOf 搜索数组,无法自定义搜索条件,所以出现了 find
  • find 会对每个元素执行一次回调函数,直到找到符合条件的元素,就将这个元素返回(永远只返回一个),并结束函数执行;找不到则返回 undefined
  • 注意这个方法不会跳过 empty 元素,所以这里不做 i in arr 的检查
[1,2,3,4,5].find((item,index,arr) => item > 2)      // 3

实现

Array.prototype.myFind = function(fn,thisArg = null){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this
    for(let i = 0;i < arr.length;i++){
        const result = fn.call(thisArg,arr[i],i,arr)
        if(result){
            return arr[i]
        }
    }
    return undefined
}

findIndex

用法

和 find 基本一致,但是 findIndex 返回的是第一个符合条件的元素的索引,没有这样的元素就返回 -1

[1,2,3,4,5].findIndex((item,index) => item > 2)      // 2

实现

Array.prototype.myfindIndex = function(fn,thisArg = null){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this
    for(let i = 0;i < arr.length;i++){
        const result = fn.call(thisArg,arr[i],i,arr)
        if(result){
            return i
        }
    }
    return -1
}

filter

用法

通过 find 搜索数组,只能找到一个符合条件的元素,而 filter 可以筛选出所有符合条件的元素:

[1,2,3,4,5].filter((item,index) => item > 2)        // 返回 [3,4,5]
[1,2,3,4,5].filter((item,index) => item > 100)      // 没有符合的元素,返回 []

实现

Array.prototype.myFilter = function(fn,thisArg = null){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this
    let res = []
    let k = 0
    for(let i = 0;i < arr.length;i++){
        if(i in arr){
            const result = fn.call(thisArg,arr[i],i,arr)
            // 如果元素符合条件,则放入新数组中
            if(result){
                res[k++] = arr[i]
            }
        }
    }
}

some

用法

接受一个回调函数表示判断条件,只要数组中有一个元素满足该条件(回调函数返回 true),some 方法就返回 true,否则返回 false

[1,2,3,4].some((item,index) => item>3)      // 至少有一个大于3的数,返回 true
[1,2,3,4].some((item,index) => item>100)    // 没有一个大于100的数,返回 false

实现

Array.prototype.mySome = function(fn,thisArg = null){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this
    for(let i = 0;i < arr.length;i++){
        const result = fn.call(thisArg,arr[i],i,arr)
        if(result){
            return true
        }
    }
    return false
}

every

用法

接受一个回调函数表示判断条件,只有数组中所有元素都满足该条件(回调函数返回 true),every 方法才会返回 true,有一个不满足都会返回 false

[1,2,3,4].every((item,index) => item>0)     // 所有元素都大于0,返回 true
[1,2,3,4].every((item,index) => item>3)     // 并非所有元素都大于3,返回 false

实现

Array.prototype.myEvery = function(fn,thisArg = null){
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    let arr = this
    for(let i = 0;i < arr.length;i++){
        const result = fn.call(thisArg,arr[i],i,arr)
        if(!result){
            return false
        }
    }
    return true
}

reduce

用法

reduce 可以归并数组的每个元素,最终构建一个累计归并值作为返回值:

  • 语法:arr.reduce((acc,cur,index,arr) => {...},baseAcc)
  • 接受两个参数,一个是回调函数,一个是初始累计归并值。其中,回调的当前返回值会作为下次迭代所使用的 acc,也即累计归并值。初始累计归并值缺省是第一个非 empty 元素,且此时会从该元素的下一个元素开始迭代。
// 没有提供初始累计归并值,因此缺省是1,并且从2开始迭代
[1,2,3,4].reduce((acc,cur) => acc + cur)              // 10

// 提供100作为初始累计归并值,从1开始迭代
[1,2,3,4].reduce((acc,cur) => acc + cur,100)          // 110

// 二维数组转化为一维数组,concat 本身会拍平一维数组
[1,2,[3,4]].reduce((acc,cur) => acc.concat(cur),[])       // [1,2,3,4]

实现

实现的时候,有两个关键的地方:

  • 如何判断有没有传第二个参数?用 typeof baseAcc === 'undefined' 判断不准确,因为有可能传的第二个参数确实就是 undefined,这里可以通过剩余参数的长度判断
  • 如何找出数组的第一个非 empty 元素?遍历数组,用 in 判断
Array.prototype.myReduce = function(...args){
    let fn = args[0]
    let arr = this
    let len = arr.length
    let index = 0,acc
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not a function`)
    }
    // 如果传了第二个参数
    if(args.length >= 2){
        acc = args[1]
    } else {
        // 只要当前数组还没找到非 empty 元素,就一直遍历下去
        while(index < len && !(index in arr)){
            index++
        }
        // 如果数组是一个充满 empty 元素的空数组,则抛出错误
        if(index >= len){
            throw new TypeError('Reduce of empty array with no initial value')
        }
        // index 加一,表示第一个非 empty 元素的下一个元素
        acc = arr[index++]
        for(;index < len;index++){
            if(index in arr){
                acc = fn(acc,arr[index],index,arr)
            }
        }
        return acc
    }    
}

reduceRight

用法

用法基本和 reduce 一致,区别是它是从后往前去遍历数组的。

[0, 1, 2, 3].reduceRight((acc, cur) => {
  console.log(cur);
});
// 2
// 1
// 0

实现

reduceRight 的实现和 reduce 基本一样,但需要注意:非 empty 元素的查找以及数组的遍历顺序是反过来的

Array.prototype.myReduceRight = function(...args){
    let fn = args[0]
    let arr = this
    let len = arr.length
    let index = len - 1,acc
    if(typeof fn != 'function'){
        throw new TypeError(`${fn} is not function`)
    }
    if(args.length >= 2){
        acc = args[1]
    } else {
        while(index > 0 && !(index in arr)){
            index--
        }
        if(index == 0){
            throw new TypeError('Reduce of empty array with no initical value')
        }
        acc = arr[index--]
        for(;index >= 0;index--){
            if(index in arr){
                acc = fn(acc,arr[index],index,arr)
            }
        }
        return acc
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
手写实现 `Promise.all` 方法,可以按照以下步骤进行操作: 1. 创建一个新的 Promise 对象,并返回它。 2. 在内部创建一个数组 `results`,用于存储每个传入的 Promise 对象的结果。 3. 使用 `Array.from` 方法将传入的参数(可以是数组或类数组对象)转换为一个真正的数组。 4. 遍历数组中的每个 Promise 对象,使用 `Promise.resolve` 方法将其转换为一个 Promise 对象。 5. 对于每个 Promise 对象,使用 `.then` 方法来处理其结果。 - 如果 Promise 对象 resolved,将结果存储到 `results` 数组中。 - 如果 Promise 对象 rejected,直接将整个 `Promise.all` 的 Promise 对象 rejected,并传递该错误给它。 6. 在所有 Promise 对象都处理完毕后,如果没有任何错误,则将 `results` 数组作为参数解析传递给 `Promise.all` 的 Promise 对象,并使其 resolved。 以下是一个示例代码,展示了如何手写实现 `Promise.all` 方法: ```javascript function myPromiseAll(promises) { return new Promise((resolve, reject) => { const results = []; const promisesCount = promises.length; let resolvedCount = 0; if (promisesCount === 0) { resolve(results); return; } function handleResolve(index, value) { results[index] = value; resolvedCount++; if (resolvedCount === promisesCount) { resolve(results); } } function handleReject(error) { reject(error); } for (let i = 0; i < promisesCount; i++) { Promise.resolve(promises[i]) .then((value) => handleResolve(i, value)) .catch(handleReject); } }); } // 示例 Promise 对象 const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = new Promise((resolve) => setTimeout(resolve, 1000, 3)); // 使用自定义的 Promise.all 方法 myPromiseAll([promise1, promise2, promise3]) .then((results) => { console.log(results); // 输出: [1, 2, 3] }) .catch((error) => { console.log(error); }); ``` 通过以上代码,你可以手动实现 JavaScript 中的 `Promise.all` 方法。请注意,这只是一个简单的示例,并没有处理所有可能的情况,如传入的参数不是一个数组等。在实际使用中,建议使用 JavaScript 原生的 `Promise.all` 方法来处理多个 Promise 对象的并行操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值