原生 JavaScript 手写数组 API

本文将手写数组的常用方法

在我们常用到的forEach,map,filter,some,every,reduce….,你会发现,这些方法基本都是传入一个函数,然后对数据加工,处理,返回我们想要的结果。

map

map:

不会对空数组检测

返回一个新的数组

  • 语法
array.map(function(currentValue,index,arr), thisValue)
复制代码
Array.prototype._map = function (fn, thisArr) {
      if (this == undefined) {
        throw new TypeError('this is null or not undefined');
    }
     if (Object.prototype.toString.call(fn) !== '[object Function]') {
        throw new TypeError(fn + 'is not a function');
    }
    let res = [] 
    let mapArr = this // [1, 2, 3]
    for (let i = 0; i < mapArr.length; i++) {
        res[i] = fn.call(thisArr, mapArr[i], i, mapArr)
    }
    return res
}
let arr = [1, 2, 3]
let mapRes = arr._map((val, index, item) => {
    return val + 1
})
console.log(mapRes)// [2,3,4]

复制代码

所谓的这些个方法, 就是内部封装好了,方便使用。比如手写map,

  • 因为返回新数组,所以要声明一个空数组,然后return出去,里面做的事,就是把每一次调用fn,并且给fn赋值这件事。赋值给返回的新数组。等到你用map的时候,就是看到了具体的逻辑操作,eg: val+1。内部怎么得到的val,index,item, 每一项是怎么对应的,你不用关心

forEach

forEach:

空数组是不会执行回调函数的。

改变原数组

返回值是undefined

  • 语法
array.forEach(function(currentValue, index, arr), thisValue)
复制代码
  • 具体实现可以参考map,map是返回一个新数组,forEach是直接改变原数组。

filter

filter:

不会对空数组进行检测

返回一个新的数组

  • 语法
array.filter(function(currentValue, index, arr), thisValue)
复制代码
Array.prototype._filter = function (fn, thisArr) {
       if (this == undefined) {
        throw new TypeError('this is null or not undefined');
    }
     if (Object.prototype.toString.call(fn) !== '[object Function]') {
        throw new TypeError(fn + 'is not a function');
    }
    let filterArr = this
    let filterRes = []
    for (let i = 0; i < filterArr.length; i++) {
        if (fn.call(thisArr, filterArr[i], i, filterArr)) {
            filterRes.push(filterArr[i])
        }

    }
    return filterRes
}
let arr = [1, 2, 3]
let filRes = arr._filter((val, index, item) => {
    return val > 1
})
console.log(filRes)// [2,3]
复制代码
  • filter方法,是筛选数据。当满足条件时,返回满足条件的值

  • 满足条件 : if (fn.call(thisArr, filterArr[i], i, filterArr)) {}

  • 满足条件的值: filterRes.push(filterArr[i])

some

some:

不会对空数组进行检测

返回一个新的数组

  • 语法
array.some(function(currentValue, index, arr), thisValue)
复制代码
Array.prototype._some = function (fn, thisArr) {
      if (this == undefined) {
        throw new TypeError('this is null or not undefined');
    }
     if (Object.prototype.toString.call(fn) !== '[object Function]') {
        throw new TypeError(fn + 'is not a function');
    }
    let someArr = this
    for (let i = 0; i < someArr.length; i++) {
        if (fn.call(thisArr, someArr[i], i, someArr)) {
            return true
        }

    }
    return false
}
let arr = [1, 2, 3]
let someRes = arr._some((val, index, item) => {
    return val > 1
})
console.log(someRes)// true
复制代码
  • some也是筛选数据,只是他返回的是true/false。如果有一个元素满足条件,则返回true , 剩余的元素不会再执行检测

every

  • every也是筛选数据。只是他返回的是true/false。如果所有元素都满足条件,则返回true , 只要有一个元素不满足条件,就返回false
Array.prototype._every = function (fn, thisArr) {
       if (this == undefined) {
        throw new TypeError('this is null or not undefined');
    }
     if (Object.prototype.toString.call(fn) !== '[object Function]') {
        throw new TypeError(fn + 'is not a function');
    }
    let everyArr = this
    for (let i = 0; i < everyArr.length; i++) {
        if (!fn.call(thisArr, everyArr[i], i, everyArr)) {
            return false
        }
    }
    return true

}
let arr = [1, 2, 3]
let everyRes = arr._every((val, index, item) => {
    return val > 0
})
console.log(everyRes)// true
复制代码
  • every和some的实现,我觉得很有意思

  • every是所有的满足条件,才返回true。所以要先判断

  • if (!fn.call(thisArr, everyArr[i], i, everyArr)) {
    return false
    }

  • some是只要有一个满足条件,就返回true.所以要先判断

  • if (fn.call(thisArr, everyArr[i], i, everyArr)) {
    return true
    }

  • 你品,你细品?

reduce

  • reduce 法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
  • 语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
复制代码
Array.prototype._reduce = function (fn, init) {
      if (this == undefined) {
        throw new TypeError('this is null or not undefined');
    }
     if (Object.prototype.toString.call(fn) !== '[object Function]') {
        throw new TypeError(fn + 'is not a function');
    }
    let reduceArr = this
    let index = arguments.length === 1 ? 1 : 0 // 求索引。如果没有传初始值,那么index就是1,因为没有传初始值,prev就是初始值,下标是0,那么自然curr下标就是1
    let prev = arguments.length === 1 ? reduceArr[0] : init // 求初始值。如果没有传入初始值,那么初始值就是数组的第一项,否则就是传入的初始值init
    for (let i = index; i < reduceArr.length; i++) {
        prev = fn(prev, reduceArr[i], i, reduceArr)  // 迭代累加
    }
    return prev
}
let arr = [1, 2, 3]
let reduceRes = arr._reduce((prev, curr) => {
    return prev + curr
})
console.log('reduceRes', reduceRes)// 6
let reduceResParams = arr._reduce((prev, curr) => {
    return prev + curr
}, 2)
console.log('reduceResParams', reduceResParams)// 8
复制代码
  • argument是类数组,用于接收传入的不定参数,如果argument.length === 1 代表没有传入参数,可自行输出观察
  • reduce的index是一个需要注意的点,因为如果传入初始值,那么当前值curr是从第一个算起,所以index是0,如果没有传入初始值,那么curr是从第二个开始算起,所以index是1
  • reduce的prev初始值。如果传入初始值,那么初始值是init, 没有传入初始值,初始值则是数组的第一个元素reduceArr[0]
  • reduce作为一个累加器,可以想成是 a = a+ 1的简单逻辑.换成代码就是
prev = fn(prev, reduceArr[i], i, reduceArr)  // 迭代累加 
复制代码

includes

  • 方法用于判断字符串是否包含指定的子字符串。

  • 如果找到匹配的字符串则返回 true,否则返回 false。

  • 语法

String.includes(searchvalue, start)
Array.includes(searchvalue, start)
复制代码
Array.prototype._includes = function (searchElement, formIndex = 0) {
    let includesEle = this
    let len = includesEle.length
    if (searchElement.length >= len || !len) return false // 如果传入起始位大于数据的长度,或者没有数据的
    for (let i = formIndex; i < len; i++) {
        if (includesEle[i] === searchElement) // 当传入的数据和查找的数据一致,返回true
            return true
    }
    return false
}
let arr = [1, 2, 3]
let includes = arr._includes(1)
console.log('includes', includes)// true
复制代码
  • 这里判断了len的存在,和 if (this == undefined) {
    throw new TypeError(‘this is null or not undefined’);
    }是一个道理

indexOf

  • indexOf(item, start) start为开始检索位置,找到返回下标值,没找到返回-1

  • 他的实现和includes可以说一模一样。includes 是返回true/ false ,那么indexOf中,true就是返回数组下标,false就是返回-1

rray.prototype._indexOf = function (searchElement, formIndex = 0) {
    let indexOf = this
    let len = indexOf.length
    if (searchElement.length >= len || !len) return -1 // 如果传入起始位大于数据的长度,或者没有数据的
    for (let i = formIndex; i < len; i++) {
        if (indexOf[i] === searchElement) // 当传入的数据和查找的数据一致,返回true
            return i
    }
    return -1
}
let arr = [1, 2, 3]
let _indexOf = arr._indexOf(3)
let _indexOf2 = arr._indexOf(33)
console.log('_indexOf', _indexOf)// 2
console.log('_indexOf2', _indexOf2)// -1


复制代码

总结

  • 在所以的方法里,都是传入一个函数,那么首先就是要判断是否是函数

    Object.prototype.toString.call(fn) !== '[object Function]'
    
    typeof fn !== 'function'
    复制代码
  • 我们在用的过程中,其实thisValue这个参数,一般都不写,所以在文中遇到fn.call(thisArr, filterArr[i], i, filterArr) 可直接写成fn(filterArr[i], i, filterArr) 。我这里是为了迎合他的用法?

thisValue:可选。传递给函数的值一般用 “this” 值。
如果这个参数为空, “undefined” 会传递给 “this” 值

  • 关于this,我们记的是,谁调用他,他就指向谁,他在指向的同时,其实,this也代表了指向的那个数据。比如let filterArr = this 这是一种很好的获取调用者数据的方式
  • 返回的是新数据,所以要声明一个空数组 然后将其return出去
  • 以上手写,发现有很多相似之处
Array.prototype._every = function (fn, thisArr) {
    if (this == undefined) {
        throw new TypeError('this is null or not undefined');
    }
     if (Object.prototype.toString.call(fn) !== '[object Function]') {
        throw new TypeError(fn + 'is not a function');
    }
    let everyArr = this
    for (let i = 0; i < everyArr.length; i++) {
        
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享