JS 理清数组的方法

338 阅读7分钟

前言

  • 在 js 中,数组是最常使用的对象之一
  • 数组是值的有序集合,每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引

数组的创建

字面量创建

var empty = [];     
var arr = [1.1, true, "a",];    

new Array() 构造函数创建

console.log(new Array()) // []
console.log(new Array('protein')) // ['protein']
console.log(new Array(3)) // [ , , ] // Number 类型参数作为 length 长度处理

Array.of() 方法创建

console.log(Array.of('protein')) // ['protein']
console.log(Array.of(3))// [3] // 区别构造函数创建,参数即为数组的元素

Array.from() 方法创建

  • 方法从一个类似数组或者可迭代对象创建一个新的浅拷贝的数组实例
  • 伪数组对象:拥有 length 属性和若干索引属性的对象
  • 可迭代对象:拥有 iterator 接口,可以获取对象中的元素比如 Map Set 等
  • Array.from(arrayLike[, mapFn[, thisArg]]) => map 遍历 arrayLike 执行回调函数
// 从 String 生成数组
console.log((Array.from('protein'))) // ["p", "r", "o", "t", "e", "i", "n"]

// 从 Set 生成数组
const set1 = new Set(['protein', 'potato', 'tomato'])
console.log(Array.from(set1)) // [ 'protein', 'potato', 'tomato' ]

// 从 Map 生成数组
const fruits = new Map([['apple', 2], ['banana', 3]])
console.log(Array.from(fruits)) // [ [ 'apple', 2 ], [ 'banana', 3 ] ]

// 从类数组对象(arguments)生成数组
function fun1() {
	return Array.from(arguments)
}
console.log(fun1(1, 2, 3)) // [ 1, 2, 3 ]

// 函数
// 遍历 [1, 2, 3] 执行回调函数 x + y,两个值为 map 函数的参数即 x 为遍历的每一项值 y 为下标值
console.log(Array.from([1, 2, 3], (x, y) => x + y)) // [ 1, 3, 5 ]
// 遍历 [1, 2, 3] 执行回调函数 x * 2,x 值为 map 函数的参数
console.log(Array.from([1, 2, 3], x => x * 2)) // [ 2, 4, 6 ]

// 数组去重合并
function combine() {
	let arr = [].concat.apply([], arguments)
	return Array.from(new Set(arr))
}
let arr1 = [1, 2, 2, 3]
let arr2 = [2, 3, 3, 3, 4]
console.log(combine(arr1, arr2)) // [ 1, 2, 3, 4 ]

会改变原数组的那些方法

Array.prototype.push()

  • 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
let fruits = ['tomato', 'apple']
let total = fruits.push('pear')
console.log(fruits) // [ 'tomato', 'apple', 'pear' ]
console.log(total) // 3
total = fruits.push('banana', 'orange')
console.log(fruits) // [ 'tomato', 'apple', 'pear', 'banana', 'orange' ]
console.log(total) // 5

Array.prototype.pop()

  • 方法从数组中删除最后一个元素,返回该元素的值,此方法会更改原数组的长度
let fruits = ['tomato', 'apple', 'pear']
let lastone = fruits.pop()
console.log(lastone) // pear
console.log(fruits) // [ 'tomato', 'apple' ]

Array.prototype.unshift()

  • 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度

Array.prototype.shift()

  • 方法从数组中删除第一个元素,返回该元素的值,此方法会更改原数组的长度

Array.prototype.splice()

  • 方法通过删除或者替换现有元素或者原地添加新的元素来修改数组
  • 接收三个参数,第一个代表起始位置,第二个代表要改变的个数,第三个代表改变后的值
  • 区别 slice 方法会改变原数组
let arr2 = ['apple', 'orange']
arr2.splice(1, 0, 'banana')
console.log(arr2) // [ 'apple', 'banana', 'orange' ]
arr2.splice(1, 1) 
console.log(arr2) // [ 'apple', 'orange' ]
arr2.splice(1, 1, 'pear')
console.log(arr2) // [ 'apple', 'pear' ]

Array.prototype.reverse()

  • 方法将数组中元素的位置颠倒,并返回新的数组

Array.prototype.sort()

  • 对元素进行排序,默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的
  • 方法接收一个可选回调函数为参数
let arr1 = [2, 4, 1, 6, 3, 5, 10, 22, 9]
console.log(arr1.sort()) // [1, 10, 2, 22, 3, 4, 5, 6, 9]
let arr2 = arr1.sort((x, y) => x - y)
console.log(arr2) // [1, 2, 3, 4, 5, 6, 9, 10, 22]

不会改变原数组的那些方法

  • 下面这些方法不会改变调用他们的对象的值,会返回一个新的数组或者期望值
Array.prototype.concat()
  • 方法用于合并两个或者多个数组,该方法不会修改原来的数组,而是返回一个新数组
let arr3 = [1, 2, 3]
let arr4 = ['a', 'b', 'c']
let arr5 = arr3.concat(4, arr4, 5)
console.log(arr5) // [1, 2, 3, 4, "a", "b", "c", 5]

Array.prototype.slice()

  • 该方法截取一个新数组返回
  • 接收参数 begin 表示截取起始位置和可选参数 end 表示截取结束位置
  • 区别 splice 方法第二个代表数量的参数以及没有第三个参数
let arr8 = ['a', 'b', 'c']
let arr9 = arr8.slice(1)
console.log(arr9) // [ 'b', 'c' ]
console.log(arr8) // [ 'a', 'b', 'c' ]

Array.prototype.join()

  • 连接数组所有元素并返回一个字符串
  • 接收一个可选参数用来连接字符串
let arr7 = ['a', 'b', 'c']
console.log(arr7.join('')) // abc
console.log(arr7.join()) // a,b,c
console.log(arr7.join('-')) // a-b-c

Array.prototype.toString()

  • 等同于调用 join 方法不带参数
let arr10 = ['a', 'b', 'c']
console.log(arr10.toString()) // a,b,c
console.log(arr10.join()) // a,b,c

检测元素的方法

Array.prototype.includes()

  • 方法用来判断一个数组是否包含指定的值,返回 boolean
  • 接收第二个参数为查找的位置,如果超出数组的长度结果返回 false,如果是负数则从 arr.length - index 位置开始查找
let arr6 = ['a', 'b', 'c']
console.log(arr6.includes('a')) // true
console.log(arr6.includes('a', 1)) // false
console.log(arr6.includes('a', 100)) // false
console.log(arr6.includes('a', -1)) // false
console.log(arr6.includes('a', -100)) // true

Array.prototype.indexOf()

  • 方法返回给定元素的索引位置,如果不存在则返回 -1
  • 该方法接收第二个参数代表查找的起始位置,如果大于数组的 length 则返回 -1,如果为负数则从该数组的 length + index 位置开始查找
let arr11 = ['a', 'b', 'c']
console.log(arr11.indexOf('a')) // 0
console.log(arr11.indexOf('a', 1)) // -1
console.log(arr11.indexOf('a', 100)) // -1
console.log(arr11.indexOf('a', -1)) // -1
console.log(arr11.indexOf('a', -100)) // 0

Array.prototype.lastIndexOf()

  • 方法返回值同 indexOf 方法为索引位置,不存在返回 -1
  • 第二个参数同 indexOf 方法为查找的起始位置,只不过不同于 indexOf 方法的起始位置 0 它的默认起始位置为 arr.length - 1,如果大于数组的 length 则查找整个数组,如果为负数则从该数组的 length + index 位置向左查找
let arr12 = ['a', 'b', 'c', 'a']
console.log(arr12.lastIndexOf('a')) // 3
console.log(arr12.lastIndexOf('a', 10)) // 3
console.log(arr12.lastIndexOf('a', 3)) // 3
console.log(arr12.lastIndexOf('a', 2)) // 0
console.log(arr12.lastIndexOf('a', -2)) // 0
console.log(arr12.lastIndexOf('a', -1)) // 3

检测数组类型的方法

Array.isArray()

  • 用来判断传递的值是不是一个数组
  • 可以用 constructor 构造函数来检测还可以用 Object 原型上的 toString 方法返回
  • Polyfill 如下:
if(!Array.isArray) {
	Array.isArray = function(arg) {
		return Object.prototype.toString.call(arg) === '[object Array]'
	}
}

迭代遍历方法

Array.prototype.every()

  • 测试数组内的元素是否都符合 cb 的测试,返回一个 boolean 值
  • 当所有的元素符合条件才会返回 true
let arr13 = [5, 6, 7, 8, 9]
let result13 = arr13.every(o => o > 4)
console.log(result13) // true
let result131 = arr13.every(o => o > 5)
console.log(result131) // false

Array.prototype.some()

  • 区别 every 方法,它只要有一个元素符合条件就会返回true
let arr14 = [5, 6, 7, 8, 9]
let result14 = arr14.some(o => o > 8)
console.log(result14) // true
let result141 = arr14.some(o => o > 9)
console.log(result141) // false

Array.prototype.filter()

  • 方法创建一个新数组,将通过测试的元素放进去
let arr15 = [5, 6, 7, 8, 9]
let result15 = arr15.filter(o => o > 7)
console.log(result15) // [ 8, 9 ]

Array.prototype.forEach()

  • 区别于 map 和 reduce 方法,它返回 undefined
  • 不可进行链式调用
  • 被调用时不会改变原数组,但是可以被它的 cb 函数改变
  • 除了抛出异常,我们无法停止或者跳出 forEach 循环

Array.prototype.map()

  • map 区别于 forEach 生成一个新的数组,如果你不打算使用返回的新数组或者没有从回调函数中返回值,建议使用 forEach 或者 for of 循环遍历, 同 forEach 不会改变原数组
let arr16 = [1, 2, 3]
let arr161 = arr16.map(o => o * 2)
console.log(arr161) // [ 2, 4, 6 ]
console.log(arr16) // [ 1, 2, 3 ]

Array.prototype.reduce()

  • 为每个元素执行 cb 函数
  • cb 函数接收两个参数 pre 和 cur 值,如果是第一次调用 pre 的值为 cb 的第二个参数或者第一个元素的值,否则为cb的返回值
  • 第三个参数为正在处理的值的索引
let arr17 = [1, 2, 3]
let result17 = arr17.reduce((x, y) => x + y)
console.log(result17)
let result171 = arr17.reduce((x, y) => x + y, 10)
console.log(result171)
let result172 = arr17.reduce((x, y, i) => {
   console.log(i) // 1, 2
   return x + y
})
let result173 = arr17.reduce((x, y, i) => {
   console.log(i) // 0, 1, 2
   return x + y
}, 10)
  • 数组求和
let arr = [1, 2, 3, 4]
let sum = arr.reduce((x, y) => x + y)
console.log(sum) // 10
  • 计算数组中每个元素出现的次数
let arr = ['apple', 'orange', 'tomato', 'potato', 'banana', 'apple']
let result = arr.reduce((pre, cur) => {
	if(cur in pre) {
		pre[cur]++
	} else {
		pre[cur] = 1
	}
	return pre
}, {})
console.log(result) // {apple: 2, orange: 1, tomato: 1, potato: 1, banana: 1}
  • 数组去重
let arr = ['apple', 'orange', 'tomato', 'potato', 'banana', 'apple']
let result = arr.reduce((pre, cur) => {
	if(!pre.includes(cur)) {
		return pre.concat(cur)
	} else {
		return pre
	}
}, [])
console.log(result) // ["apple", "orange", "tomato", "potato", "banana"]
  • 数组降维
// 2 - 1
let arr = [[1, 2], [3, 4]]
let result = arr.reduce((pre, cur) => {
	return pre.concat(cur)
}, [])
console.log(result) // [1, 2, 3, 4]

// more - 1
let arr = [[1, 2], [3, 4], [5, 6, [7, 8, [9, 10]]]]
let transFun = (arr) => {
	return arr.reduce((pre, cur) => {
		return pre.concat(Array.isArray(cur) ? transFun(cur) : cur)
	}, [])
}
console.log(transFun(arr)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • 对象里的属性求和
var arr = [{
        name: 'xiaohong',
        age: 10
    }, {
        name: 'xiaohua',
        age: 20
    }, {
        name: 'xiaoli',
        age: 30
    }]
let sum = arr.reduce((pre, cur) => {
	return pre + cur.age
}, 0)
console.log(sum) // 60

Array.prototype.reduceRight()

  • 用法同 reduce 区别于从右开始遍历操作数组
let arr18 = [1, 2, 3]
let result18 = arr18.reduceRight((x, y) => x + y)
console.log(result18)
let result181 = arr18.reduceRight((x, y) => x + y, 10)
console.log(result181)
let result182 = arr18.reduceRight((x, y, i) => {
	console.log(i) // 1, 0
	return x + y
})
let result183 = arr18.reduceRight((x, y, i) => {
	console.log(i) // 2, 1, 0
	return x + y
}, 10)