深入数组之手写数组api

这是我参与新手入门的第3篇文章

上篇文章对数组api进行了梳理,在整体上对数组api有了清晰的了解。接下来这篇文章将手动实现数组的部分api,从底层上来认识认识它们是怎么实现的…

手写every

先上源码

     Array.prototype._every = function(callBack, thisArg) {
      if(this == null) {
        throw new TypeError()
      }
      if(typeof callBack !== 'function') {
        throw new TypeError()
      }
      //问题1
      //if(thisArg == null) {
      //  thisArg = window
      //}
      const obj = Object(this)
      const len = obj.length >>> 0 // 问题2
      for(let i = 0; i < len; i++) {
        if(i in obj && !callBack.call(thisArg, obj[i], i, obj)) {
          return false
        }
      }
      return true //若其是一个空数组,则直接返回true
    }
复制代码

基本思路如下:

  1. 安全性考虑,对于thiscallBack是否符合类型,进行判断。
  2. 将this抽象为对象(通用性), 同时获取转换后的length
  3. 核心操作,在遍历数组时跳过空项,若存在一个不满足callBack的返回false

对于代码中出现的问题解答:

问题1: 为什么可以不考虑thisArg为null或者undefined的情况?
因为代码中是使用了call来实现绑定this的。其本身内部就考虑了thisArgnull或者undefined的情况。不了解call的可以看手写call,apply

问题2:obj.length >>> 0有什么用?
>>> 0有3个作用:(1)、将非数字类型转换为number; (2)、将数字类型转换为32位 无符号 整数。 所以,它还有个副作用,对于负数x来说会将其转换为2^32 + x

手写filter

源码如下:

    Array.prototype._filter = function(callBack, thisArg) {
      if(this == null) {
        throw new TypeError()
      } 
      if(typeof callBack !== 'function') { 
        throw new TypeError()
      }
      // if(thisArg == null) {
      //   thisArg = window //严格模式下为undefined
      // }
      const obj = Object(this)
      let len = obj.length >>> 0
      const result = new Array(len) // 预先分配长度
      let index = 0
      for(let i = 0; i < len; i++) {
        if(i in this && callBack.call(thisArg, obj[i], i, obj)) {
          result[index++] = obj[i]
        }
      }
      result.length = index //调整长度为合适状态
      return result
    }
复制代码

思路基本没变…

手写reduce

源码:

    Array.prototype._reduce = function(callBack, initialValue) {
      if(this == null) {
        throw new TypeError()
      }
      if(typeof callBack !== 'function') {
        throw new TypeError()
      }
      const obj = Object(this)
      const len = obj.length >>> 0
      
      //问题1
      const hasInit = arguments.length > 1
      
      if(len === 0 && !hasInit) {
        throw new TypeError()
      }
       //问题2
      let accumulator, i = 0
      if(hasInit) {
        accumulator = initialValue
      } else {
        while(i < len  && !(i in obj)) {
          i++
        }
        if(i >= len) {
          throw new TypeError()
        }
        accumulator = obj[i++]
      }
      while(i < len) {
        if(i in obj) {
          // 问题3
          accumulator = callBack(accumulator, obj[i], i, obj)
        }
        ++i
      }
      return accumulator
    }
复制代码

对于代码中出现的问题解答:

问题1:怎么判断是否提供了initialValue呢?使用initialValue !== undefined判断行不行?
不能使用initialValue !== undefined来判断是否提供了initialValue。其无法判断initialValue赋值为undefined的情况。

image.png
问题2: reduce内部对于没有提供initialValue时,accmulator的初值什么?
注意:当没有提供initialValue时,对于accmulator的初值是第一个已赋值的项(空数组报错)。其不会考虑未赋值的。我在初次写代码的时候没有考虑到这,就离谱…

问题3:accumulator的值是怎么修改的?
注意:accumulator是每次迭代,callBack的返回值赋值给accumulator的。也就是说accumulator是由callBack的返回值确定的!

image.png
由于用的少.开始还以为accumulator是通过某种响应式操作处理的呢…有点憨0.0

手写includes

源码:

    Array.prototype._includes = function(value, fromIndex) {
      if(this == null) {
        throw new TypeError()
      }
      const obj = Object(this)
      const len = obj.length >>> 0
      if(len === 0) { //如果是空数组则直接返回false
        return false
      }
      //问题1
      fromIndex = fromIndex | 0
      //问题2
      let n = Math.max(fromIndex < 0 ? len - Math.abs(fromIndex) : fromIndex, 0)
      const sameValueZero = (x, y) => x === y || (x !== x && y !== y)
      while(n < len) {
        if(sameValueZero(value, obj[n])) {
          return true
        }
        n++
      }
      return false
    }
复制代码

对于代码中出现的问题解答:

问题1:fromIndex = fromIndex | 0有什么用?
fromIndex | 0有两个作用:(1)、将fromIndex转换为number类型.  (2)、将fromIndex转换为32位 有符号 整数。注意是有符号

问题2:如果fromIndex是负数,该怎么处理?
从代码可知,若fromIndex是负数,则使用length - Math.abs(fromIndex)替换。之后再判断,如果length - Math.abs(fromIndex)小于0,则再用0替换.

最后

这是新人活动最后一篇了。对于这三篇文章,自我感觉是真好,现实也是真残酷。不知道还能不能拿到新人优秀奖…如果觉得不错还请点个赞,帮个忙…十分感谢!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享