如果数组最后一项为空删除最后一项和去前面的逗号_手写数组方法

最近把数组的常用方法都手写了一遍,整理到了一块儿,顺便就分享出来。

注:有的方法的实现算法可能不是最好的,所以也可以根据自己的理解有不一样的实现方法,欢迎在评论区留出来。

我废话就不多说了,直接上代码,大概有20个左右,代码比较长,每一个方法我都注释了自己的实现思路,难理解的地方也有代码注释。

/* push方法
* 1.push接收参数个数无限制,可以是字符串、数值或引用类型
* 2.获取当前调用push方法的原数组的长度
* 3.遍历参数,每遍历一个参数,原数组长度加1,并把这个参数赋给这个新的索引,最后返回原数组长度
*/
Array.prototype._push = function () {
  let arr = this
  if (!Array.isArray(arr)) throw new Error('not Array')
  let args = [...arguments]
  let length = args.length
  let arrL = arr.length
  if (length === 0) return arrL
  for (let i = 0; i < length; i++) {
    arr[arrL++] = args[i]
  }
  return arrL
}

/** pop
* 1.设置原数组最后一项为null
* 2.原数组长度减1,返回被删除的项
*/  
Array.prototype._pop = function () {
  let arr = this
  if (!Array.isArray(arr)) throw new Error('not Array')
  let length = arr.length
  if (length === 0) return
  let lastItem = arr[length - 1]
  arr[length - 1] = null
  arr.length--
  return lastItem
}

/** unshift
 * 1.接收不限个数的参数
 * 2.获取原数组长度
 * 3.把原数组所有项后移一位
 * 4.遍历参数,每遍历一个参数,数组长度加1,在数组头部添加该参数
 */

Array.prototype._unshift = function () {
  let arr = this
  if (!Array.isArray(arr)) throw new Error('not Array')
  let args = [...arguments]
  let length = args.length
  let arrL = arr.length
  if (length === 0) return arrL
  for (let j = arrL; j > 0; j--) {
    // 把所有元素往后移动参数个数位
    arr[j + length - 1] = arr[j - 1]
  }
  // 然后把参数对号入座
  for (let i = 0; i < length; i++) {
    arr[i] = args[i]
    arrL++
  }
  return arrL
}

/** shift
 * 1.不接受参数
 * 2.把原数组中所有的项向前移一位
 * 3.原数组长度减一
 */  
  
Array.prototype._shift = function () {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (arr.length === 0) return
  let result = arr[0]
  for (let i = 0; i < arr.length; i++) {
    arr[i] = arr[i + 1]
  }
  arr.length--
  return result
}

/** concat
 * 1. 接收不限个数参数,类型可以是数组或其他类型
 * 2. 获取原数组长度,遍历concat的参数,如果参数是数组则拆开,如果数组中的元素还包括数组,不用一一拆分
 * 3. 每遍历一个,原数组长度加1,并作为索引,将当前元素添加到该索引上,为了完全达到手写目的,
 *    这里不采用push方法,而是for循环遍历赋值,真实环境下肯定是现有push才有concat,所以面试时可以直接用push
 * 4. 不会改变原数组,要用到深拷贝,不调用slice或其他方法,用for
 */  
Array.prototype._concat = function () {
  let args = [...arguments]
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (args.length === 0) {
    return arr
  }
  let len = arr.length
  let resultArr = []
  // 深拷贝一份原数组
  for (let n = 0; n < len; n++) {
    resultArr[n] = arr[n]
  }
  for (let i = 0; i < args.length; i++) {
    if (Array.isArray(args[i])) {
      for (let j = 0; j < args[i].length; j++) {
        resultArr[len++] = args[i][j]
      }
    } else {
      resultArr[len++] = args[i]
    }
  }
  return resultArr
}

/* isAarry
* 直接用最安全的方法,简单粗暴
*/
Array.prototype._isArray = function (arr) {
  if (Object.prototype.toString.call(arr) !== '[object Array]') {
    return false
  }
  return true
}

/** toString
 * 循环遍历逗号分隔即可  
 */
Array.prototype._toString = function () {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  let str = ''
  let length = arr.length
  if (length === 0) {
    str = ''
  }
  for (let i = 0; i < length; i++) {
    if (i === length - 1) {
      str += arr[i]
    } else {
      str += arr[i] + ','
    }
  }
  return str
}

/** join
 * 类似toString,只是加了个分隔符号而已
 */
Array.prototype._join = function (point) {
  let arr = this
  let p = point || ','
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  let str = ''
  let length = arr.length
  if (length === 0) {
    str = ''
  }
  for (let i = 0; i < length; i++) {
    if (i === length - 1) {
      str += arr[i]
    } else {
      str += arr[i] + p
    }
  }
  return str
}

/** indexOf
 * 1.接收两个参数分别为查找的元素和起始位置,起始位置可选,默认从第一项开始查找
 * 2.遍历原数组,用全等去匹配每一项,如果匹配成功就返回当前元素下标,如果一直没匹配成功,则返回-1 
 */
Array.prototype._indexOf = function (ele, start) {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (arr.length === 0) return -1
  let e = ele
  let s = start || 0
  let result = -1
  for (let i = s; i < arr.length; i++) {
    if (e === arr[i]) {
      result = i
      break
    }
  }
  return result
}

/** forEach
 * 1.接收一个函数参数和一个函数执行作用域的参数(可选),参数函数也接受3个参数,分别是当前项值,当前项索引(可选),当前数组(可选)
 * 2.执行参数函数,默认执行环境为window,不返回任何值
 */
Array.prototype._forEach = function (fn, scope) {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (typeof fn !== 'function') {
      throw new Error('argument error')
    }
  if (arr.length === 0) return
  let sp = scope || window
  for (let i = 0; i < arr.length; i++) {
    fn.call(sp, arr[i], i, arr)
  }
}

/** map
 * 1.接收一个函数参数和一个函数执行作用域的参数(可选),参数函数也接受3个参数,分别是当前项值,当前项索引(可选),当前数组(可选)
 * 2.执行参数函数,默认执行环境为window,把参数函数返回的结果push到一个新的数组中,最后把这个新的数组返回到外部
 */
Array.prototype._map = function (fn, scope) {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (typeof fn !== 'function') {
    throw new Error('argument error')
  }
  if (arr.length === 0) return []
  let sp = scope || window
  let result = []
  for (let i = 0; i < arr.length; i++) {
    let back = fn.call(sp, arr[i], i, arr)
    result.push(back)
  }
  return result
}

/** filter
 * 1.接收一个函数参数和一个函数执行作用域的参数(可选),参数函数也接受3个参数,分别是当前项值,当前项索引(可选),当前数组(可选)
 * 2.执行参数函数,默认执行环境为window,把参数函数返回为true的项push到一个新的数组中,最后把这个新的数组返回到外部
 */ 
Array.prototype._filter = function (fn, scope) {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (typeof fn !== 'function') {
    throw new Error('argument error')
  }
  if (arr.length === 0) return []
  let sp = scope || window
  let result = []
  for (let i = 0; i < arr.length; i++) {
    let back = fn.call(sp, arr[i], i, arr)
    if (back) {
      result.push(arr[i])
    }
  }
  return result
}

/**
 * 1. 接收一个函数参数和一个函数执行作用域的参数(可选),参数函数也接受3个参数,分别是当前项值,当前项索引(可选),当前数组(可选)
 * 2. 执行参数函数,默认执行环境为window,如果返回值全为true,最终的every返回true,当有一个返回false时,退出循环,立即返回false 
 */  

Array.prototype._every = function (fn, scope) {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (typeof fn !== 'function') {
    throw new Error('argument error')
  }
  if (arr.length === 0) return false
  let sp = scope || window
  let result = true
  for (let i = 0; i < arr.length; i++) {
    let back = fn.call(sp, arr[i], i, arr)
    if (!back) {
      result = false
      break;
    }
  }
  return result
}

/**
 * 1. 接收一个函数参数和一个函数执行作用域的参数(可选),参数函数也接受3个参数,分别是当前项值,当前项索引(可选),当前数组(可选)
 * 2. 执行参数函数,默认执行环境为window,如果返回值有一个为true,退出循环,最终的every返回true,当全部返回false时,最终返回false
 */  
Array.prototype._some = function (fn, scope) {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (typeof fn !== 'function') {
    throw new Error('argument error')
  }
  if (arr.length === 0) return false
  let sp = scope || window
  let result = false
  for (let i = 0; i < arr.length; i++) {
    let back = fn.call(sp, arr[i], i, arr)
    if (back) {
      result = true
      break
    }
  }
  return result
}


/* reduce:
  1.接收函数参数和一个初始值,初始值默0,参数函数也有四个参数,分别是上一次计算的值,当前项,当前项索引,当前数组
  2.遍历数组,依次把当前项与上一项之和,当前项的值,当前项索引,当前数组传入参数函数
  3.执行参数函数,并把归并后的值保存下来,提供给下一项使用
*/
Array.prototype.myReduce =  function (fn, init) {
  if (!Array.isArray(this)) {
    throw new Error('not Array')
  }
  if (typeof fn !== 'function') {
    throw new Error('argument error')
  }
  var arr = this
  var total = init || 0
  for (var i = 0; i < arr.length; i++) {
    total = fn(total, arr[i], i, arr)
  }
  return total
}

/** sort
 * 1.接收一个比较函数作为参数,比较函数接收两个参数
 * 2.遍历原数组,如果存在参数函数,获取参数函数的返回值,如果返回值为1,则前一项移至后一项的后面,如果为-1,则前一项排列在前面,如果等于0,位置不变
 * 3.如果不存在参数函数,就按照字符串比较大小,按从小到大排列,返回排列后的数组
 */  

Array.prototype._sort = function () {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error("not Array")
  }
  let compare = [...arguments]
  let length = compare.length
  if (length === 0) {
    for (let i = 0; i < arr.length - 1; i++) { // 两两相邻互换
      for (let j = 0; j < arr.length - 1 -i; j++) {
        if (arr[j] > arr[j+1]) {
          let temp = arr[j+1]
          arr[j+1] = arr[j]
          arr[j] = temp
        }
      }
    }
  }
  if (length > 0) {
    let fn = compare[0]
    for (let i = 0; i < arr.length - 1; i++) { // 两两相邻互换
      for (let j = 0; j < arr.length - 1 -i; j++) {
        let result = fn(arr[j], arr[j+1])
        if (result < 0) {
          let temp = arr[j+1]
          arr[j+1] = arr[j]
          arr[j] = temp
        } else {
          continue
        }
      }
    }
  }
}


/** reverse
 * 1.不接收参数
 * 2.循环一次,循环长度为原数组长度一半,两端位置互换
 * 
 */  
Array.prototype._reverse = function () {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (arr.length === 0) {
    return arr
  }
  for (let i = 0, len = arr.length; i < len/2; i++) {
    let temp = arr[i]
    arr[i] = arr[len - 1 -i]
    arr[len - 1 -i] = temp
  }
  return arr
}

/****************   最难的两个来了,各位挺住    *************/

/** slice
 * 1.接收两个参数,第二个参数可选,如果不传第二个参数,默认原数组长度,即一直截取到末尾
 * 2.从第一个参数开始遍历原数组,直到结束位置时结束遍历,把遍历的每一项push到一个新的数组中,返回这个新数组
 * 3.如果第一个参数是负数,则起始位置为数组长度 + 第一个参数。结束位置的下标不能小于或等于起始位置下标,否则返回空数组
 */
Array.prototype._slice = function (start, end) {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (arr.length === 0) {
    return []
  }
  let s = start
  // 如果开始位置或结束的位置小于零,则从数组末尾项向前数倒数的第几位开始或结束
  if (start < 0) {
    s = start + arr.length
  }
  let e = end || arr.length
  if (end < 0) {
    e = end + arr.length
  }
  // 如果结束的位置比开始的位置小,则直接返回空数组
  if (e < s) {
    return []
  }
  let result = []
  for (let i = s; i < e; i++) {
    result.push(arr[i])
  }
  return result
}

/** splice
 * 1.有三个功能,删除、替换、插入,根据参数不同来进行操作
 * 2.如果是插入或替换,只需用替换元素长度减去删除的个数即可得出向后移动的位置数,把插入替换的元素在移动出空缺的位置上对号入座;
 *   如果是删除,则从起始下标与删除个数之和作为向前移动的开始下标,向前移动删除个数个位置
 * 3.位置查询,如果第一个参数是正数,则是从前往后执行,如果是负数,则是从后往前执行
 * 4.改变原数组,返回删除的值组成的数组
 */  

Array.prototype._splice = function () {
  let arr = this
  if (!Array.isArray(arr)) {
    throw new Error('not Array')
  }
  if (arguments.length === 0) {
    return []
  }
  let idx // 第一个参数,起始下标
  // 如果传入的起始下标小于零,就从末尾倒数,如果下标在倒数时超过了数组长度,则直接设为0
  if (arguments[0] < 0) {
    idx = arr.length + arguments[0]
    if (idx < 0) idx = 0
  } else {
    idx = arguments[0]
  }
  let num = arguments[1] // 第二个参数,删除的个数
  // 第三个及以后的参数,要添加或替换到原数组的元素,这里直接用了slice,可以用for挨个遍历
  let ele = Array.prototype.slice.call(arguments, 2)
  if (num > arr.length - idx) {
    // 如果传入的删除个数大于了起始位置到末尾位置之间元素的个数,最多删除到元素组最后一个元素
    num = arr.length - idx
  }
  let result = []
  for (let i = idx; i < idx + num; i++) {
    // 要返回删除的项
    result.push(arr[i])
  }
  if (ele.length > 0) {
    // 第三个参数存在,则是插入或替换,插入或替换
    for (let i = arr.length-1, len = ele.length - num; i >= idx; i--) {
      // 从idx开始所有元素向后移len个长度
      if (len < 0) len = 0 // 防止覆盖起始下标之前的元素
      arr[i + len] = arr[i]
    }
    // 从起始位置开始将插入或替换的元素对号入座
    for (let j = 0; j < ele.length; j++) {
      arr[idx++] = ele[j]
    }
  } else {
    // 删除,后面的元素往前移num个位置,length-num
    for (let i = idx; i < arr.length; i++) {
      arr[i] = arr[i + num]
    }
    arr.length = arr.length - num
  }
  return result;
}

/************** 赠送 **********************/

/** instanceof
 * 1.接收两个参数,一个是instanceof左边,一个是右边
 * 2.如果左边实例的__proto__等于右边构造函数的prototype,那么就直接返回true,否则继续遍历实例的__proto__的__proto__
 */
function _instanceof (L, R) {
  let r = R.prototype
  let l = L.__proto__
  while(true) {
    if (l === null) {
      return false
    }
    if (r === l) {
      return true
    }
    l = l.__proto__
  }
}

/**
 * 1.接收两个参数,第一个参数是数组,第二个参数是需要展开的层级
 * 2.reduce遍历数组,flag限制递归次数,用concat去连接归并的结果
 *  
 */  
function flatten (array, fg) {
  if (!Array.isArray(array) || array.length === 0) {
    return
  }
  let flag = fg || 0
  return array.reduce(function (last, ele) {
    let result = []
    // infnaty表示无限拍平
    if (flag === 'infnaty') {
      return last.concat(Array.isArray(ele) ? flatten(ele, 'infnaty') : ele)
    } else {
      return flag > 0 ? last.concat(Array.isArray(ele) ? flatten(ele, --flag) : ele) : last.concat(ele)
    }
  }, [])
}

ok,就这些,还是那句话,有自己更好的实现方式的,可以提出来,多谢啦~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值