一文搞懂JS数组

200 阅读4分钟

一文搞懂JS数组

本文带你梳理javascript中最常用、最灵活、方法繁多的数据结构---数组

生成数组的几种方式

// 1.通过构造函数生成
new Array(1,2,3,4,5) // [1, 2, 3, 4, 5]
new Array(3) // [empty × 3]
// 2.通过字面量方式
let arr = [1,2,3,4,5]
let arr = []; arr.length = 5 // [empty × 5]
// 3.通过ES6新方法生成
Array.of(1,2,3,4,5) // [1, 2, 3, 4, 5]
Array.of(5) // [5] 这是和构造函数生成最大的区别
// 4. Array.from用于将类数组转换为数组
function fun1() {
    console.log(Array.from(arguments))
}
fun1(1,2,3,4,5) // [1, 2, 3, 4, 5]
let obj = {'0': 'a', '1': 'b', '2': 'c', length: 3}
Array.from(obj, function(value, key) { // 进阶用法
        return value + 'd'
    },
    obj // 第三个参数为this指向
) // ['ad', 'bd', 'cd']

判断数组的方式

常用的判断数组的方法有以下几种:

// 1.Object.prototype.toString.call
Object.prototype.toString.call([]) === '[object Array]' // true
// 2.instanceof
[] instanceof Array // true
// 3.constructor
[].constructor === Array // true
// 4.isPrototypeof
Array.prototype.isPrototypeOf([]) // true
// 5.getPrototypeof
Object.getPrototypeOf([]) === Array.prototype // true
// 6.Array.isArray
Array.isArray([]) // true

数组自身的方法

数组的方法非常多,我们先简单分为两类:改变自身的和不改变自身的

改变自身的方法

let arr = [1,2,3,4,5]
// 1.push
arr.push(6) // 返回6 arr: [1, 2, 3, 4, 5, 6]
// 2.pop
arr.pop() // 返回5 arr: [1,2,3,4]
// 3.shift
arr.shift() // 返回1 arr: [2,3,4,5]
// 4.unshift
arr.unshift(6) // 返回6 arr: [6,1,2,3,4,5]
// 5.reverse
arr.reverse() //  返回[5,4,3,2,1] arr: [5,4,3,2,1]
// 6.sort
let arr = [1,3,2,5,4]
arr.sort() // 返回[1,2,3,4,5] arr: [1,2,3,4,5]
// 7.splice 替换或删除
arr.splice(1,1,4) // 返回[2] arr: [1,4,3,4,5]
// 8.copyWithin  复制指定区间元素到指定位置
arr.copyWithin(0,2,4) // 返回[3,4,3,4,5] arr: [3,4,3,4,5]
// 9.fill 在指定位置填充指定数据
arr.fill(10, 0, 3) // 返回[10,10,10,4,5] arr: [10,10,10,4,5]

不改变自身的方法

// 1.concat
let arr = [1,2,3,4,5]
arr.concat([6,7,8]) // [1, 2, 3, 4, 5, 6, 7, 8]
// 2.join
arr.join() // 1,2,3,4,5
// 3.slice
arr.slice(1,3) // [2,3]
// 4.toString
arr.toString() // 1,2,3,4,5
// 5.toLocaleString
arr.toLocaleString() // 1,2,3,4,5
// 6.indexOf
let arr = ['a','b','c','d','e']
arr.indexOf('c') // 2
// 7.includes
arr.includes(3) // true

遍历数组的方法

遍历数组的方法也非常多,不会改变自身

let arr = [1,2,3,4,5]
let obj = {name: 'mf'}
// 1.forEach
let res = arr.forEach(function(value, index, arr) {
    arr[index] = value; 
    console.log(this.name) // mf
}, obj)
console.log(res) // undefined
console.log(arr) // [1,2,3,4,5]
// 2.every
const res = arr.every((value, index, array) => {return value > 0})
console.log(res)  // true
// 3.some
const res = arr.some((value, index, array) => {return value > 3}) 
console.log(res) // true
// 4.map
let res = arr.map((value, index) => {return value + 1})
console.log(res) // [2, 3, 4, 5, 6]
// 5.filter
let res = arr.filter((value, index) => {return value > 3})
console.log(res) // [4, 5]
// 6.reduce
let res = arr.reduce(function(prev, val, idx, array) {return prev + val})
console.log(res) // 15
// 7.find和findIndex
let arr = [1,8,3,4,5,6]
let res = arr.find(function(value, index, array) {return value % 2 === 0})
console.log(res) // 8
let res = arr.findIndex(function(value, index, array) {return value % 2 === 0})
console.log(res) // 1

类数组

类数组是js中比较特别的一类对象,在平时写代码中也会经常遇到

  • 函数的arguments对象
  • 获取DOM的一些api拿到的结果:document.getElementsByTagName("div")、document.getElementsByClassName("a")、document.getElementsByName()、document.querySelector()

下面我们一一来了解它们的特点

arguments

function fun1(a,b,c){
    console.log(arguments)
}
fun1('aaa', 'bbb', 'ccc')
// 打印
// Arguments(3) ['aaa', 'bbb', 'ccc', callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 0: "aaa"
// 1: "bbb"
// 2: "ccc"
// callee: f fun1(abc)
// length: 3
// ......

可以看到arguments获取的是一个类似数组对象,并且有length属性

其中还有个callee属性,这个指向函数本身,

arguments主要应用场景是获取函数参数,特别是在参数个数不明确的时候 比如:

function fun1() {
    let res = 0; 
    len = arguments.length; 
    for(let i = 0; i < len; i++) {
        res += arguments[i]
    } 
    return res
}
fun1(1,2,3,4,5) // 15

另外一种使用场景,拼接参数字符串

function myConcat(separe) {
    let args = Array.prototype.slice.call(arguments, 1); 
    return args.join(separe)
}
myConcat("|", 'a', 'b','c','d') // 'a|b|c|d'

第三种使用场景,参数传递

function fun2(a,b,c) {
    console.log(a,b,c)
}
function fun1() {
    fun2.apply(this, arguments)
}
fun1(1,2,3) // 1 2 3

类数组转换为数组

第一种,借助数组的能力

function transformArray() {
    return Array.prototype.slice.call(arguments)
}
transformArray(1,2,3,4,5) // [1,2,3,4,5]
function transformArray2() {
    return Array.prototype.concat.apply([], arguments)
}
transformArray2(1,2,3,4,5) // [1,2,3,4,5]

第二种,ES6提供的API

function fun() {
    return Array.from(arguments)
}
fun(1,2,3,4,5) // [1, 2, 3, 4, 5]
functtion fun2() {
    return [...arguments]
}
fun2(1,2,3,4,5) // [1, 2, 3, 4, 5]

数组的应用实践

数组拍平

数组拍平是指将多维数组变成一维数组,最容易想到的就是遍历加递归

function myFlatten(arr) {
    let res = []
    for (let i = 0; i < arr.length; i++) {
        Array.isArray(arr[i]) ? res = res.concat(myFlatten(arr[i])) : res.push(arr[i])
    }
    return res
}
myFlatten([1,2,[3,4],5]) //  [1, 2, 3, 4, 5]
function myFlatten2(arr) {
    return arr.reduce((prev, curr, array) => {
        return prev.concat(Array.isArray(curr) ? myFlatten(curr) : curr)
    }, [])
}
myFlatten2([1,2,[3,4],5]) //  [1, 2, 3, 4, 5]
function myFlatten3(arr) {
    while(arr.some(item => Array.isArray(item))) {
        return [].concat(...arr)
    }
    return arr
}
myFlatten3([1,2,[3,4],5]) //  [1, 2, 3, 4, 5]

下面来看下其他的实现方式

// 先来看看数组调用toString()会生成什么
[1,2,[3,4,[5,6]],7].toString() // '1,2,3,4,5,6,7'
// 看到这个就好办了
function myFlatten4(arr) {
    return arr.toString().split(',')
}
myFlatten4([1,2,[3,4],5]) //  [1, 2, 3, 4, 5]
// ES6提供的flat方法, 参数为要展开的层级
[1,2,[3,4,[5,6]],7].flat(1) // [1, 2, 3, 4, [5, 6], 7]
[1,2,[3,4,[5,6]],7].flat(Infinity) // [1, 2, 3, 4, 5, 6, 7]
function myFlatten5(arr) {
    return arr.flat(Infinity)
}
// 最后一种方式,用正则来实现
// 思路:先将多维数组转换为字符串,然后通过正则过滤掉字符串中的中括号
function myFlatten6(arr) {
    let str = arr.toString()
    str = str.replace(/(\[|\])/g, '')
    str = '[' + str + ']'
    return JSON.parse(str)
}
myFlatten6([1,2,[3,4,[5,6]],7]) //  [1, 2, 3, 4, 5, 6, 7]

总结

  • 生成数组的方式:字面量、构造函数、Array.of()、Array.from()
  • 判断数组的方式:Object.prototype.toString.call、instanceof、constructor、isPrototypeof、getPrototypeof、Array.isArray
  • 数组自身的方法
    • 改变自身的方法:push、pop、shift、unshift、splice、reverse、sort、copWithin、fill
    • 不改变自身的方法:slice、concat、join、toString、toLocalString、indexof、isArray
  • 遍历数组的方法:forEach、map、every、some、filter、reduce、find、findIndex
  • 类数组
    • 常见的类数组:函数参数列表、DOM API获取的元素列表
    • 将类数组转换为数组的方式:通过call或者apply借助数组的能力、ES6提供的Array.from
  • 数组拍平的方式
    • 普通遍历+递归方式:forEach、reduce、while
    • 其他方式:toString().split(',')、ES6中的flat、正则