数组是值的有序集合,其中的值叫做元素,每个元素有一个数值表示的位置,叫做索引。JavaScript数组是无类型限制的,即数组中的元素可以是任意类型,同一数组的不同元素也可以是不同的类型。
数组简介

数组是值的有序集合,其中的值叫做元素,每个元素有一个数值表示的位置,叫做索引。JavaScript数组是无类型限制的,即数组中的元素可以是任意类型,同一数组的不同元素也可以是不同的类型。数组元素从第一个索引值0开始,到最大索引值4 294 967 294,也就是说数组可以包含4 294 967 294个元素。JavaScript数组是动态的,它们会按需增大或缩小,因此创建数组无需声明一个固定大小,也无需再大小变化时重新为它们分配空间。

数组从Array.prototype继承属性。

ES6增加了“定型数组”,与常规的数组不同,该数组具有固定长度和固定的数值元素类型,定型数组具有极高的性能,支持对二进制数据的字节级访问。

创建数组

数组字面量

let empty = [] // 空数组
let primes = [1, 2, 3, 4, 5] // 同类型的数组
let array = [1.1, true , 'a', primes] // 不同类型的元素

数组字面量可以包含对象字面量或其他数组字面量:

let array = [ [ 1, { x: 1, y: 2 } ], [ 2, { x: 3, y: 4 } ] ]

扩展操作符

数组可以使用扩展操作符——...在一个数组字面量中包含另一个数组的元素:

let a = [1, 2, 3]
let b = [0, ...a, 4] // b == [0, 1, 2, 3, 4]

扩展操作符适用于任何可迭代对象。字符串是可迭代对象,因此可以使用扩展操作符把任意字符串转换为单个字符的数组:

let digits = [...'0123456789']
console.log(digits) // 0,1,2,3,4,5,6,7,8,9

集合对象是可迭代的,因此要去除数组中的重复元素,一种便捷方式就先把数组转换为集合,然后在使用扩展操作符把这个集合转换回数组:

let o = [...'hello world']
let set = [...new Set(o)]
console.log(set.toString()) // h,e,l,o, ,w,r,d

Array()构造函数

构造函数可以传入参数,指定数组的长度。或者传入多个参数作为新数组的元素。

let a = new Array(10) // 指定长度为10
let a = new Array(1, 2, 3, 4, 'hello', true) // 传入的参数作为新数组的元素

Array.of()

Array.of()函数可以使其参数值作为数组元素来创建并返回新数组:

Array.of() // 空数组
Array.of(10) // [10]
Array.of(1, 2, 3) // [1, 2, 3]

Array.from()

Array.from(iterable)期待一个可迭代对象(Set、Map、类数组对象和字符串)作为参数,并返回包含该对象元素的新数组。

除了可以接收第一个参数以外,第二个是一个回调函数:

// 字符串
Array.from('foo')
// Array ["f", "o", "o"]

// 回调函数
Array.from('foo', (value) => {
  console.log(value)
})
// f o o

// set集合
const set = new Set(['foo', 'bar', 'baz', 'foo'])
Array.from(set);
// [ "foo", "bar", "baz" ]

// map集合
const map = new Map([[1, 2], [2, 4], [4, 8]])
Array.from(map);
// [[1, 2], [2, 4], [4, 8]]

// 类数组对象
function f() {
    return Array.from(arguments)
}
let array = f(1, 2, 3)

// [1, 2, 3]
添加和删除数组元素

给新索引赋值:

let a = []
a[0] = 'zero'
a[1] = 'one'

push()方法在数组末尾添加一个或多个元素:

let a = []
a.push('zero', 'one', 'two')

delete操作符删除数组元素:

let a = [1, 2, 3]
delete a[2]

a.length // 3,delete删除元素不修改数组长度

对数组元素使用delete操作符不会修改length属性,也不会把高索引位的元素向下移动来填充被删除属性的空隙,而splice()是一个可以插入、删除或替换数组元素的通用方法,通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容,此方法会改变原数组:

在指定索引位置添加一个元素:

const months = ['Jan', 'March', 'April', 'June']
months.splice(1, 0, 'Feb') // ["Jan", "Feb", "March", "April", "June"]

替换指定索引位置处的元素:

const months = ['Jan', 'March', 'April', 'June']
months.splice(4, 1, 'May') // ["Jan", "Feb", "March", "April", "May"]
迭代数组

array.entries()

entries() 方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对:

let str = [...'hello world']
for (let [index, value] of str.entries()) {
    console.log(value)
}

forEach()

forEach((currentValue, index, array) => {}) 方法按升序为数组中含有效值的每一项执行一次回调函数,那些已删除或者未初始化的项将被跳过。

currentValue是指当前迭代的元素,index是当前迭代的索引,array是被循环的数组。

let str = [...'hello world']
str.forEach((currentValue, index, array) => {
    console.log(currentValue)
})
数组方法

迭代器方法

map()

map()方法会给原数组中的每个元素都按顺序调用一次回调函数。回调函数每次执行后的返回值(包括 undefined)组合起来形成一个新数组。因为map()生成一个新数组,当你不打算使用返回的新数组却使用map是违背设计初衷的,要么使用forEach或者for-of替代。

let array = [1, 2, 3, 4, 5]
let dest = array.map((currentValue) => {
    currentValue = currentValue * 2
    return currentValue
})

filter()

filter() 方法创建一个新数组,该数组包含调用它的数组的子数组。回调函数用来测试数组的每个元素的函数,返回 true 表示该元素通过测试,保留该元素,false 则不保留。

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']
const result = words.filter((word) => { return word.length > 6 }) // ["exuberant", "destruction", "present"]

find()和findIndex()

find()和findIndex()在找到第一个元素时停止迭代,find返回匹配的元素,findIndex返回匹配元素的索引值:

const words = [
    'spray',
    'limit',
    'elite',
    'exuberant',
    'destruction',
    'present'
]
const result = words.find((word) => {
    return word.length > 6
})
// exuberant,一旦满足一次true,结束查询,并返回数组元素

const index = words.findIndex((word) => {
    return word.length > 6
})
// 3,一旦满足一次true,返回数组元素的索引值

every()和some()

every()方法为数组中的每个元素执行一次回调函数,直到它找到一个会使回调返回 false 的元素。如果发现了一个这样的元素,every()方法将会立即返回 false。否则,回调函数会为每一个元素返回 true:

const words = [
    'spray',
    'limit',
    'elite',
    'exuberant',
    'destruction',
    'present'
]
const result = words.every((word) => {
    return word.length > 6
})

// false,第一个元素——spray字符长度小于6,不满足条件,停止遍历

some() 为数组中的每一个元素执行一次回调函数,直到找到一个使得回调函数返回一个true。如果找到了这样一个值,some() 将会立即返回 true。否则,some() 返回 false:

const words = [
    'spray',
    'limit',
    'elite',
    'exuberant',
    'destruction',
    'present'
]
const result = words.some((word) => {
    return word.length > 6
})

// true,函数找到一个符合字符长度大于6的元素,停止遍历

归并数组元素

reduce()和reduceRight()使我们指定的函数归并数组元素,最终产生一个值。有时候也称为注入或折叠。

reduce((accumulator, currentValue, currentIndex) => {}, initialValue)是低索引值向高索引值递增(升序),回调函数的第一个参数accumulator是累加之后的值,第二个参数currentValue是当前迭代的元素,第三个参数currentIndex是当前迭代的元素在数组的索引值。

如果reduce()提供了initialValue,accumulator的初始值为initialValue,currentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。

let a = [1, 2, 3, 4, 5]
let result = a.reduce((accumulator, currentValue, currentIndex) => {
    return accumulator + currentValue
}, 0) // 15
a.reduce((x, y) => x * y, 1) // 120
a.reduce((x, y) => (x > y) ? x : y) // 5

reduceRight((accumulator, currentValue, index) => {}, initialValue)是高索引值向低索引值递减(降序),回调函数的第一个参数accumulator是累加之后的值,第二个参数currentValue是当前迭代的元素,第三个参数currentIndex是当前迭代的元素在数组的索引值。

如果reduce()提供了initialValue,accumulator的初始值为initialValue;如果没有提供 initialValue,那么accumulator取数组中的最后一个值。

let a = [1, 2, 3, 4, 5]
let result = a.reduceRight((accumulator, currentValue, currentIndex) => {
    return accumulator + currentValue
}, 0) // 15

打平数组

flag()方法用于创建并返回一个新数组,无参调用时,flag()会打平一级嵌套,如果打平更多层级,需要给方法传递数值参数:

let a = [1, 2, 3, [4, 5, 6, [7, 8, 9]]]
let b = a.flat() // 1级:[1, 2, 3, 4, 5, 6, [7, 8, 9]]
let c = a.flat(2) // 2级:[1, 2, 3, 4, 5, 6, 7, 8, 9]

flagMap()方法返回的数组会自动打平一级,a.flagMap()等同于a.map().flat():

let array = ["it's Sunny in", '', 'California']
array.map((value) => value.split(' ')) // [["it's","Sunny","in"],[""],["California"]]
array.flatMap((value) => value.split(' ')) // ["it's","Sunny","in", "", "California"]

// array.map()等同于array.map().flat()
array.map((value) => value.split(' ')).flat() // ["it's","Sunny","in", "", "California"]

添加数组

concat()方法创建并返回一个新数组,新数组包含原数组,以及传给concat()的参数。如果这些参数中有数组,则拼接的是它们的元素而非数组本身:

let a = [1, 2, 3]
let b = a.concat([4, 5], [6, 7]) // [1, 2, 3, 4, 5, 6, 7]

concat()创建并返回的新数组不会被打平:

let a = [1, 2, 3]
let b = a.concat(4, [5, [6, 7]]) // [1, 2, 3, 4, 5, [6, 7]]

concat()并不修改调用它的数组:

let a = [1, 2, 3]
let b = a.concat(4, 5)
console.log('b', b) // b (5) [1, 2, 3, 4, 5],创建新数组b
console.log('a', a) // a (3) [1, 2, 3],不修改数组a

实现栈和队列操作

push()

push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度:

const animals = ['pigs', 'goats', 'sheep']
animals.push('cows') // 4
animals.push('chickens', 'cats', 'dogs') // 7

push().apply()可以合并两个数组:

let vegetables = ['parsnip', 'potato']
let moreVegs = ['celery', 'beetroot']
// 将第二个数组融合进第一个数组
// 相当于 vegetables.push('celery', 'beetroot');
// Array.prototype.push.apply(vegetables, moreVegs)
vegetables.push.apply(vegetables, moreVegs) // ["parsnip", "potato", "celery", "beetroot"]

pop()

pop() 方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度:

const plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato']
console.log(plants.pop()) // "tomato"

shift()

shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度:

const array = [1, 2, 3];
const firstElement = array.shift() // 1

unshift()

unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组):

const array = [1, 2, 3]
console.log(array.unshift(4, 5)) // 5

// [4, 5, 1, 2, 3]

数组切片

数组定义了几个处理连续区域的方法,即数组的提取、替换、填充和复制切片的方法。

slice()

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

const animals = ['ant', 'bison', 'camel', 'duck', 'elephant']

console.log(animals.slice(2))
// ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4))
// ["camel", "duck"]

console.log(animals.slice(1, 5))
// ["bison", "camel", "duck", "elephant"]

console.log(animals.slice(-2))
// ["duck", "elephant"]

console.log(animals.slice(2, -1))
// ["camel", "duck"]

splice()

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组:

const months = ['Jan', 'March', 'April', 'June']
months.splice(1, 0, 'Feb') // 在index为1处插入一个元素,["Jan", "Feb", "March", "April", "June"]
months.splice(4, 1, 'May') // 替换index为4处的元素为May,["Jan", "Feb", "March", "April", "May"]
months.splice(2, 2) // 删除索引值为[2, 3]的元素, ["Jan", "Feb", "May"]

fill()

第一个参数是替换的元素,第二个参数是其实索引,第三个是终止索引(不包括终止索引处的元素)。如果终止索引为负数,那么终止索引=数组长度-终止索引;如果没有终止索引,那么将会替换到数组的最后一个元素为止。

const array = [1, 2, 3, 4]
console.log(array.fill(0, 2, -1)) // 终止索引为-1,则end为length-end,即end为3,因此替换index为2,但不包括终止索引3,
// [1, 2, 0, 0]

console.log(array.fill(5, 1)) // 没有终止索引,替换元素为5,从index为1开始直到最后一个元素
// [1, 5, 5, 5]

console.log(array.fill(6)) // 全部元素替换为6
// [6, 6, 6, 6]

数组到字符串的转换

join()方法把数组的所有元素转换为字符串,如果指定一个字符串参数,用于分隔结果字符串中的元素,如果不指定分隔符,则默认使用逗号:

let a = [1, 2, 3]
a.join() // "1,2,3"
a.join('-') // "1-2-3"

toString()方法与没有参数的join()方法一样:

let a = [1, 2, 3]
a.toString() // "1,2,3"