JS

JS深入浅出之数组操作

Posted by weite122 on 2018-12-04

join

  • 先从最简单的数组操作开始:

    1
    2
    var array = ['a','b','c']
    array.join('-') // 结果是 'a-b-c'
  • 我们画一下内存图:

  1. array.join 实际上是 Array.prototype.join 对应的函数(array.join === Array.prototype.join === ADDR401)
  2. array.join(’-’) 等价与 array.join.call(array, ‘-’)
  3. join 函数通过 this 和 arguments[0] 可以得到 array 和 ‘-’ 两个值
  • 猜测array.join 的源码:

    1
    2
    3
    4
    5
    6
    7
    8
    Array.prototype.join = function(char){
    let result = this[0] || ''
    let length = this.length
    for(let i=1; i< length; i++){
    result += char + this[i]
    }
    return result
    }

slice

  • 用法:

    1
    array.slice(beginIndex, endIndex)
  • 猜测源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Array.prototype.slice = function(begin,end){
    let result = []
    begin = begin || 0
    end = end || this.length
    for(let i= begin; i < end; i++){
    result.push(this[i])
    }
    return result
    }
  • slice可以将伪数组转化为数组

    1
    2
    array = Array.prototype.slice.call(arrayLike)
    array = [].slice.call(arrayLike)
  • ES6 有新的方法解决上面问题

    1
    array = Array.from(arrayLike)
  • PS: 伪数组和真数组的区别就是原型链有没有Array.prototype,即操作数组的API

sort

  • 猜测源码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Array.prototype.sort = function(fn){
    fn = fn || (a,b) => a-b
    let roundCount = this.length -1
    for(let i = 0; i < roundCount; i++){
    let minIndex = this[i]
    for(let k= i + 1; k < this.length; k++){
    if(fn.call(null, this[k],this[i]) < 0) {
    [this[i], this[k]] = [this[k], this[i]]
    }
    }
    }
    }

splice

  • 用法:

    1
    arr.splice(start, count, addElement1, addElement2, ...);
    • splice的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
  • 猜测源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Array.prototype.splice = function(startIndex, deleteCount, ...params){
    let removeArray = this.slice(startIndex,startIndex+deleteCount)
    let endArray = this.slice(startIndex+deleteCount)
    let originalLength = this.length

    for(let i = 0;i < params.length; i++ ){
    this[startIndex + i] = params[i]
    }
    let currentLength = startIndex + params.length

    for(let i = 0;i < endArray.length; i++) {
    this[currentLength + i] = endArray[i]
    }

    this.length = originalLength - deleteCount + params.length
    return removeArray
    }

forEach、 map、filter 和 reduce

forEach

  • 猜测源码:

    1
    2
    3
    4
    5
    6
    7
    Array.prototype.forEach = function(fn){
    for(let i = 0; i < this.length; i++){
    if(i in this) {
    fn.call(undefined, this[i], i, this)
    }
    }
    }
  • forEachfor 的区别有两个:

    1. forEach 不能 break
    2. forEach 用到了函数,所以每次迭代都会有一个新的函数作用域。

map

  • map方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。

  • 猜测源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Array.prototype.map = function(fn){
    let result = []
    for(let i = 0; i < this.length; i++){
    if(i in this) {
    result[i] = fn.call(undefined, this[i], i, this)
    }
    }
    return result
    }
  • PS:mapforEach 的区别就是map有返回值,forEach没有

filter

  • filter方法用于过滤数组成员,满足条件的成员组成一个新数组返回。

  • 猜测源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Array.prototype.filter = function(fn){
    let result = []
    let temp
    for(let i = 0;i < this.length; i++){
    if(i in this) {
    if(temp = fn.call(undefined,this[i], i, this)) {
    result.push(this[i])
    }
    }
    }
    return result
    }
  • fn.call()返回真值就push到返回值,没有返回真值就不push

reduce

  • reduce方法是依次处理数组的每个成员,最终累计为一个值

  • 猜测源码:

    1
    2
    3
    4
    5
    6
    7
    8
    Array.prototype.reduce = function(fn, init){
    let result = init
    for(let i = 0; i < this.length; i++){
    if(i in this){
    result = fn.call(undefined, result, this[i], i, this)
    }
    }
    }
  • map、filter 和 reduce 的区别:

  • map、filter 和 reduce 的联系:

    • map 可以用 reduce 表示:

      1
      2
      3
      4
      5
      6
      array2 = array.map( (v) => v+1 )
      可以写成
      array2 = array.reduce( (result, v)=> {
      result.push(v + 1)
      return result
      }, [] )
    • filter 可以用 reduce 表示:

      1
      2
      3
      4
      5
      6
      7
      8
      array2 = array.filter( (v) => v % 2 === 0 )
      可以写成
      array2 = array.reduce((result, v) => {
      if(v % 2 === 0){
      result.push(v + 1)
      }
      return result
      })