JavaScript 数组方法解析与实现

1,024 阅读4分钟

Array的操作方法

1. unshift()

arrayObject.unshift(newelement1,newelement2,....,newelementX)

  • 向数组开头添加一个或多个元素,并返回新的长度
  • 改变原数组
  let arr = [1,2,3]
  let len = arr.unshift(1)
  console.log(len) // 4
  console.log(arr)  // [1, 1, 2, 3]

2. shift()

arrayObject.shift()

  • 删除数组第一个元素,并返回删除的元素本身
  • 如果数组为空,则不修改素组,返回undefined
  • 改变原数组
  let arr = [1,2,3]
  console.log(arr.shift()) // 1
  console.log(arr) // [2, 3]

3. push()

arrayObject.push(newelement1,newelement2,....,newelementX)

  • 向数组末尾添加一个或多个元素,并返回新长度
  • 改变原数组
  let arr = [1,2,3]
  console.log(arr.push(1,2)) // 5
  console.log(arr) // [1, 2, 3, 1, 2]

4. pop()

arrayObject.pop()

  • 删除数组最后一个元素,并返回删除元素本身
  • 如果数组为空,则不修改数组,并返回undefined
  • 改变原素组
  let arr = [1,2,3]
  console.log(arr.pop()) // 3
  console.log(arr) // [1, 2]
  console.log([].pop()) // undefined

5. concat()

arrayObject.concat(val1,val2,......,[val3])

数组和/或值,将被合并到一个新的数组中。如果省略了所有 valueN 参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝 -MDN

  • 合并多个数组/值,返回合并后的数组
  • 不改变原数组
  let arr1 = [1,2,3], arr2 = [4,5,6], arr3 = [7]
  console.log(arr1.concat(arr2, arr3)) // [1, 2, 3, 4, 5, 6, 7]
  console.log(arr1) // [1, 2, 3]

6. splice()

arrayObject.splice(index,howmany,item1,.....,itemX)

  • 删除数组指定位置长度的元素,或添加一个、多个元素,返回删除元素
  • 添加元素可选
  • 改变原数组
  let arr1 = [1,2,3]
  console.log(arr1.splice(1,2)) // [2, 3]
  console.log(arr1) // [1]
  console.log(arr1.splice(1,0,2,3)) // []
  console.log(arr1) // [1, 2, 3]

7. slice()

arrayObject.slice(start,end)

  • 截取数组指定开始和结束下边的元素,并返回结果元素数组
  • end可选
  • 不改变原数组
  let arr1 = [1,2,3]
  console.log(arr1.slice(1, 2)) // [2]
  console.log(arr1.slice(1)) // [2, 3]
  console.log(arr1) // [1, 2, 3]

8. sort()

arrayObject.sort(sortby)

  • 对数组进行排序,返回排序后的数组
  • 改变原数组
  let arr1 = [2,1,3]
  console.log(arr1.sort()) // [1, 2, 3]
  console.log(arr1) // [1, 2, 3]

!注意

由于sort()方法是分别调用元素的toString()方法,本质上是比较字符串,因而会出现以下情况

 let arr1 = [3,10,2]
 console.log(arr1.sort()) // [10, 2, 3]
 console.log(arr1) // [10, 2, 3]

这是因为'10'和'2'会先比较'1',因而10在最前,改进方法如下,编写一个比较函数

  • 想让第一个值靠前,则返回小于0的数,相等则返回0,靠后则返回大于0的数
 let arr1 = [3,10,2]
 console.log(arr1.sort(compare)) // [2, 3, 10]
 console.log(arr1) // [2, 3, 10]
 function compare(value1, value2) { 
   return value1 - value2
} 

9. reverse()

arrayObject.reverse()

  • 反转数组,返回反转后的数组
  • 改变原数组
  let arr1 = [1, 2, 3]
  console.log(arr1.reverse()) // [3, 2, 1]
  console.log(arr1) // [3, 2, 1]

10. join()

arrayObject.join(separator)

  • 以指定字符分割数组,返回分割后的字符串
  • 若参数不填,则默认以','为分割符
  • 不改变原数组
  let arr1 = [1,2,3]
  console.log(arr1.join()) // 1,2,3
  console.log(arr1.join('!')) // 1!2!3
  console.log(arr1) // [1, 2, 3]

Array的遍历方法

1. forEach()

array.forEach(function(currentValue, index, arr), thisValue)

  • 遍历数组每一个元素,并在回调函数中处理
  • 不改变原数组
  let arr = [1,2,3], newArr = []
  arr.forEach(item => newArr.push(++item))
  console.log(arr) // [1, 2, 3]
  console.log(newArr) // [2, 3, 4]

2. map()

array.map(function(currentValue,index,arr), thisValue)

  • 遍历数组每一项,并返回操作后的数组
  • 不改变原数组
  • thisValue 绑定回调函数的this上下文
  let arr = [1,2,3], newArr = []
  newArr = arr.map(function (item, index, array) {
    console.log(this) // {a: 1}
    return ++item
  }, {a: 1})
  console.log(arr) // [1, 2, 3]
  console.log(newArr) // [2, 3, 4]

3. filter()

array.filter(function(currentValue,index,arr), thisValue)

  • 遍历数组每一项,并在回调中处理,返回符合条件的新数组
  • 不改变原数组
  let arr = [1,2,3], newArr = []
  newArr = arr.filter(item => item >=2)
  console.log(arr) // [1, 2, 3]
  console.log(newArr) // [2, 3]

4. every()

array.every(function(currentValue,index,arr), thisValue)

  • 遍历数组每一项,通过回调判断是否符合条件
  • 若有一项不符合,则返回false,不继续执行;若全部符合则返回true
  • 不改变原数组
  let arr = [1,2,3], newArr = []
  let isTrue = arr.every(item => item > 0)
  console.log(arr) // [1, 2, 3]
  console.log(isTrue) // true

如何判断是否为Array

isArray()

  let arr = [1, 2, 3]
  Array.isArray(arr) // true

instanceof

  • 原理:左侧被检测对象的原型链上是否包含右侧构造函数的prototype属性
  let arr = [1, 2, 3]
  arr instanceof Array // true
  Object instanceof Object // true
  Function instanceof Object // true
  Object instanceof Function // true

constructor

  • constructor 属性返回对象的构造函数, 数组对象的构造函数为Array
  let arr = [1, 2, 3]
  arr.constructor === Array // true
  arr.constructor // ƒ Array() { [native code] }

不同对象的constructor

   function f () {}
   console.log([].constructor)    // ƒ Array() { [native code] }
   console.log({}.constructor)    // ƒ Object() { [native code] }
   console.log(f.constructor)     // ƒ Function() { [native code] }
   console.log((123).constructor) // ƒ Number() { [native code] }
   console.log('123'.constructor) // ƒ String() { [native code] }
   console.log(true.constructor)  // ƒ Boolean() { [native code] }

手动实现Array的方法

Array.map()

本身用法:

Array.map(function(item, index, arr){}, thisValue)

  • 返回处理后的新数组
  • map本身接受两个参数,一个函数,一个对象上下文
  • 函数内三个参数分别为:元素本身,元素下标、原数组
  • 对象上下文的作用是绑定函数的this指向

实现原理:

Array.prototype.map = function (fn, context) {
    let newArray = []
    for(let i=0;i<this.length;i++){
        // 通过call()来绑定回调函数的上下文
        newArray.push(fn.call(context, this[i], i, this)) 
    }
    return newArray
}
  • fn为回调函数,context为传入的对象上下文
  • 我们给fn使用call()来绑定fn的上下文,call立即执行,并将执行结果push到新数组中

Array.filter()

本身用法:

Array.filter(function(item, index ,array){}, thisValue)

  • 返回符合条件的新数组
  • map本身接受两个参数,一个函数,一个对象上下文
  • 函数内三个参数分别为:元素本身,元素下标、原数组
  • 对象上下文的作用是绑定函数的this指向

实现原理:

Array.prototype.filter = function (fn, context) {
    let newArray = []
    for (let i=0;i<this.length;i++) {
        if(fn.call(context, this[i], i, this)) {
            newArray.push(this[i])
        }
    }
    return newArray
}
  • 因为fn.call(···)返回是布尔类型,所以要先进行判断,若为真则添加到新数组中