Array
- ECMAScript 的数组是一组有序的数据(和其他语言相同),每个槽位可以存储任意类型的数据(和其他语言不同)
- ECMAScript 的数组是动态大小的,随着数据添加而自动增长
相关代码 →
创建数组
- 使用 Array 构造函数
let colors = new Array()
console.log(colors)
- 给构造函数传入一个参数:若参数是数值,则 length 属性会被自动创建并设置为这个值;若参数不是数值,则创建只包含该参数的数组
colors = new Array(10)
console.log(colors)
colors = new Array(true)
console.log(colors)
- 给构造函数传入多个参数,会自动创建包含这些参数的数组
colors = new Array('red', 'blue')
console.log(colors)
colors = Array(5)
console.log(colors)
- 使用数组字面量表示法(同对象字面量表示法,使用数组字面量表示法创建数组时,不会调用 Array 构造函数)
colors = ['red', 'blue', 3]
colors = []
- ES6 新增
Array.from()
,将类数组结构转换为数组实例
- 第一个参数是一个类数组对象,即任何可迭代结构或者包含 length 属性和可索引元素的结构
console.log(Array.from('Matt'))
console.log(Array.from(new Map().set(1, 2).set(3, 4)))
console.log(Array.from(new Set().add(1).add(2).add(3).add(4)))
const a1 = [1, 2, 3, 4]
const a2 = Array.from(a1)
console.log(a2)
console.log(a1 === a2)
const a3 = a1
console.log(a1 === a3)
const iter = {
*[Symbol.iterator]() {
yield 1
yield 2
yield 3
yield 4
},
}
console.log(Array.from(iter))
function getColors() {
console.log(Array.prototype.slice.call(arguments))
console.log(Array.from(arguments))
}
getColors('red', 'blue')
const arrayLikeObject = {
0: 1,
1: 2,
2: 3,
3: 4,
length: 4,
}
console.log(Array.from(arrayLikeObject))
- 第二个可选参数是一个映射函数,可直接增强新数组的值
const a4 = Array.from(
a1,
(x) => x + 2
)
console.log(a4)
- 第三个可选参数是一个对象,用于指定映射函数中 this 的值,此时映射函数不要用箭头函数
const a5 = Array.from(
a1,
function (x) {
console.log(this)
return x ** this.exponent
},
{ exponent: 2 }
)
console.log(a5)
const a6 = Array.from(a1, (x) => x ** this.exponent, { exponent: 2 })
console.log(a6)
const a7 = Array.from(a1, (x) => this, { exponent: 2 })
console.log(a7)
- ES6 新增
Array.of()
,将一组参数转换为数组
console.log(Array.of(1, 2, 3, 4))
console.log(Array.of(undefined, null))
创建数组 |
语法 |
参数 |
构造函数 |
let arr = new Array(3) |
单个数值/单个非数值/多个 |
数组字面量 |
let arr = ['red','blue'] |
|
Array.from() |
Array.from('Matt') |
① 类数组对象 ② 映射函数 ③ 指定映射函数 this 值的对象 |
Array.of() |
Array.of(1, 2, 3, 4) |
一组参数 |
数组空位
- 使用数组字面量初始化数组时,可以使用一串逗号来创建空位
let options = [, , , , ,]
console.log(options.length)
console.log(options)
- 与之前的版本不同,ES6 新增的方法普遍将这些空位当成 undefined 元素
options = [1, , , , 5]
console.log(options)
for (const option of options) {
console.log(option === undefined)
}
for (const [index, value] of options.entries()) {
console.log(value)
}
console.log([, , ,])
console.log(Array.from([, , ,]))
console.log(Array.of(...[, , ,]))
- ES6 之前的方法会忽略这个空位,但具体行为也因方法而已
console.log(options.map(() => 6))
console.log(options.join('-'))
- 由于行为不一致且存在性能隐患,实践中尽量避免使用数组空位,如确实需要,可以显示地用 undefined 代替
数组索引
- 使用中括号并提供相应值的数字索引,可取得或设置数组的值
- 索引小于数组包含的元素数,则返回存储在相应位置的元素
- 索引大于等于包含的元素书,则数组长度自动扩展到该索引值加 1(若中间还有元素,则自动用 undefined 填充)
colors = ['red', 'blue', 'green']
console.log(colors[0])
colors[2] = 'black'
console.log(colors)
colors[3] = 'brown'
console.log(colors)
- 数组中元素的数量保存在
length
属性中,始终返回 0 或大于 0 的值
console.log(colors.length)
- 通过修改数组的
length
属性,可以从数组末尾删除或添加元素
- 将
length
值设置为小于数组元素数的值,则只保留数组前length
位元素,剩余的末尾元素将被删除
- 将
length
值设置为大于数组元素数的值,则新添加的元素都将以undefined
填充
colors.length = 3
console.log(colors[3])
colors.length = 5
console.log(colors[5])
colors = ['red', 'blue', 'green']
colors[colors.length] = 'black'
colors[colors.length] = 'brown'
console.log(colors)
检测数组
检测数组 |
适用情况 |
instanceof |
只有一个全局作用域(只有一个网页/没有额外的 iframe) |
Array.isArray() |
任何时候(不用考虑在哪个全局上下文),首选 |
迭代器方法
- ES6 在 Array 原型上暴露了 3 个检索数组内容的方法:
keys()
、values()
、entries()
keys()
返回数组索引的迭代器
values()
返回数组元素的迭代器
entries()
返回索引/值对的迭代器
colors = ['red', 'blue', 'green']
console.log(Array.from(colors.keys()))
console.log(Array.from(colors.values()))
console.log(Array.from(colors.entries()))
for (const [i, el] of colors.entries()) {
console.log(i)
console.log(el)
}
迭代器方法 |
返回值 |
keys() |
数组索引的迭代器 |
values() |
数组元素的迭代器 |
entries() |
索引/值对的迭代器 |
复制和填充方法
- ES6 新增填充数组方法
fill()
,在指定范围内(包含开始索引,不包含结束索引),向已有数组中插入全部或部分相同的值,不改变原数组大小
- 参数一:要插入的值,非必填,若不填自动转为 undefined
- 参数二:开始索引(包含),非必填,若不填则全部填充;若负数则想象成数组长度加上这个值
- 参数三:结束索引(不包含),非必填,若不填则一直填充到数组末尾;若负数则想象成数组长度加上这个值
let zeros = [0, 0, 0, 0, 0]
zeros.fill(5)
console.log(zeros)
zeros.fill(0)
zeros.fill(6, 3)
console.log(zeros)
zeros.fill(0)
zeros.fill(7, 1, 3)
console.log(zeros)
zeros.fill(0)
zeros.fill(8, -4, 3)
console.log(zeros)
zeros.fill(0)
fill()
静默忽略:超出数组边界、零长度、索引范围方向相反,若索引部分可用则填充可用部分
zeros.fill(1, -10, -6)
console.log(zeros)
zeros.fill(0)
zeros.fill(1, 10, 15)
console.log(zeros)
zeros.fill(0)
zeros.fill(2, 4, 2)
console.log(zeros)
zeros.fill(0)
zeros.fill(4, 3, 10)
console.log(zeros)
zeros.fill(0)
- ES6 新增批量复制方法
copyWithin()
,在指定范围内(包含开始索引,不包含结束索引),浅复制数组中的部分内容,并将复制内容从指定索引开始替换,不改变原数组大小
- 参数一:从该索引开始替换,必填
- 参数二:开始索引(包含),非必填,若不填则默认为 0;若负数则想象成数组长度加上这个值
- 参数三:结束索引(不包含),非必填,若不填则一直填充到数组末尾;若负数则想象成数组长度加上这个值
zeros = [1, 2, 3, 4, 5]
zeros.copyWithin(2)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
zeros.copyWithin(4, 3)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
zeros.copyWithin(3, 1, 3)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
zeros.copyWithin(2, -4, -1)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
- 同
fill()
,copyWithin()
也会静默忽略:超出数组边界、零长度、索引范围方向相反,若索引部分可用则填充可用部分
zeros.copyWithin(2, -15, -12)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
zeros.copyWithin(2, 12, 15)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
zeros.copyWithin(2, 3, 1)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
zeros.copyWithin(2, 3, 6)
console.log(zeros)
zeros = [1, 2, 3, 4, 5]
复制和填充数组 |
含义 |
参数 |
fill() |
指定范围内向数组插入相同的值 |
① 要插入的值 ② 开始索引(包含) ③ 结束索引(不包含) |
copyWithin() |
浅复制数组指定范围内容,从指定索引开始替换 |
① 从该索引开始替换 ② 开始索引(包含) ③ 结束索引(不包含) |
转换方法
- 所有对象都有
toLocaleString()
、toString()
和valueOf()
方法:
valueOf()
返回数组本身
toString()
对数组每个值调用其toString()
方法,返回由逗号分隔拼接而成的字符串
toLocaleString()
对数组每个值调用其toLocaleString()
方法,返回由逗号分隔拼接而成的字符串
colors = ['red', 'blue', 'green']
console.log(colors)
console.log(colors.valueOf())
console.log(colors.toString())
console.log(colors.toLocaleString())
let person1 = {
toLocaleString() {
return 'Nikalaos'
},
toString() {
return 'Nicholas'
},
}
let person2 = {
toLocaleString() {
return 'Grigorios'
},
toString() {
return 'Greg'
},
}
let people = [person1, person2]
console.log(people.toString())
console.log(people.toLocaleString())
join()
对数组每个值调用其toString()
方法,接收一个参数作为设定数组返回拼接字符串的分隔符
console.log(colors.join())
console.log(colors.join(undefined))
console.log(colors.join('|'))
console.log([undefined, 1, 2].join())
数组转换 |
返回 |
参数 |
valueOf() |
数组本身 |
|
toString() |
对每个值调用toString() ,返回逗号拼接的字符串 |
|
toLocaleString() |
对每个值调用toLocaleString() ,返回逗号拼接的字符串 |
|
join() |
对每个值调用toString() ,返回指定分隔符拼接的字符串 |
指定分隔符 |
设定数组返回拼接字符串的分隔符
栈方法
- 栈是后进先出的结构,数据项的插入(push)和删除(pop)只在栈顶发生,ECMAScript 数组提供了
push()
和pop()
方法,以实现类似栈的行为
push()
方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度
colors = new Array()
console.log(colors.push('red', 'blue'))
pop()
方法用于删除数组的最后一项,返回被删除的项
console.log(colors.pop())
栈方法 |
操作 |
返回 |
参数 |
push() |
数组末尾添加项 |
数组的最新长度 |
要添加的项 |
pop() |
删除数组最后一项 |
被删除的项 |
|
队列方法
- 队列是先进先出的结构,对应的方法是
shift()
和unshift()
shift()
方法用于删除数组的开头一项,返回被删除的项
colors = new Array()
colors.push('red', 'blue')
console.log(colors.shift())
unshift()
方法接收任意数量的参数,并将它们添加到数组开头,返回数组的最新长度
console.log(colors.unshift('green', 'black'))
console.log(colors)
栈方法 |
操作 |
返回 |
参数 |
shift() |
删除数组开头一项 |
被删除的项 |
|
unshift() |
数组开头添加项 |
数组的最新长度 |
要添加的项 |
排序方法
let values = [1, 2, 3, 4, 5]
values.reverse()
console.log(values)
sort()
方法会在每一项上调用String()
转型函数,然后比较字符串按序重新排列数组元素(默认升序)
values = [0, 1, 5, 10, 15]
values.sort()
console.log(values)
sort()
方法可以接收一个比较函数,用于判断数组中数值的排列顺序,比较函数接收 2 个参数(以升序为例):
- 如果第 1 个参数应该排在第二个参数前面,则返回负值
- 如果 2 个参数相等,则返回 0
- 如果第 1 个参数应该排在第二个参数后面,则返回正值
function compareAsc(val1, val2) {
if (val1 < val2) {
return -1
} else if (val1 > val2) {
return 1
} else {
return 0
}
}
values = [0, 1, 5, 10, 15]
values.sort(compareAsc)
console.log(values)
function compareDesc(val1, val2) {
if (val1 < val2) {
return 1
} else if (val1 > val2) {
return -1
} else {
return 0
}
}
values.sort(compareDesc)
console.log(values)
values.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
console.log(values)
- 如果数组的元素是数值或 valueOf()方法返回数值的对象(如 Date 对象),比较函数可进一步简化为减法操作,用第 1 个参数减去第 2 个参数(或相反)
values.sort((a, b) => a - b)
console.log(values)
values.sort((a, b) => b - a)
console.log(values)
排序方法 |
操作 |
返回 |
参数 |
reverse() |
数组元素反向排列 |
调用数组的引用 |
|
sort() |
数组元素按序排列 |
调用数组的引用 |
比较函数,用于判断排列顺序 |
操作方法
concat()
创建当前数组的副本,然后把它的参数添加到数组末尾,返回新构建的数组,不改变原数组
- 若参数是一个或多个数组,则把这些数组的每一项都添加到结果数组末尾
- 若参数不是数组,则直接把它们添加到结果数组末尾
colors = ['red', 'green', 'blue']
let colors2 = colors.concat('yellow', ['black', 'brown'])
console.log(colors)
console.log(colors2)
let colors3 = colors.concat('yellow', ['black', 'brown', ['orange']])
console.log(colors3)
- 在参数数组上指定特殊的符号
Symbol.isConcatSpreadable
,设置为 false 可阻止打平数组(数组默认打平),设置为 true 可打平类数组对象(类数组对象默认不打平)
let newColors = ['black', 'brown']
let moreNewColors = {
0: 'pink',
1: 'cyan',
length: 2,
}
console.log(colors.concat(newColors))
console.log(colors.concat(moreNewColors))
newColors[Symbol.isConcatSpreadable] = false
moreNewColors[Symbol.isConcatSpreadable] = true
console.log(colors.concat(newColors))
console.log(colors.concat(moreNewColors))
slice()
创建一个包含原有数组中若干元素的新数组,不改变原数组,接收 1 个或 2 个参数(开始索引&结束索引)
- 1 个参数,返回该索引到数组末尾的所有元素
- 2 个参数,返回从开始索引到结束索引的所有元素,不包含结束索引
- 同
fill()
和copyWithin()
,索引负值则以数组长度加上负值即可
colors = ['red', 'green', 'blue', 'black', 'brown']
console.log(colors.slice(1))
console.log(colors.slice(1, 4))
splice()
可在数组中插入元素、删除元素或同时进行插入和删除两种操作,返回被删除元素组成的数组,改变原数组
- 删除元素,传入 2 个参数:要操作元素的开始位置、要删除元素数量
- 插入(并删除)元素,传入大于等于 3 个参数:要操作元素的开始位置、要删除元素数量(0 则不删除)、要插入的元素 1、要插入的元素 2...
colors = ['red', 'green', 'blue']
let removed = colors.splice(0, 1)
console.log(removed)
console.log(colors)
removed = colors.splice(1, 0, 'yellow', 'orange')
console.log(removed)
console.log(colors)
removed = colors.splice(1, 1, 'black', 'purple')
console.log(removed)
console.log(colors)
操作方法 |
操作 |
返回 |
参数 |
改变原数组 |
concat() |
数组末尾追加元素 |
新数组 |
要添加的元素/数组 |
否 |
slice() |
创建包含原数组若干元素的新数组 |
新数组 |
① 开始索引 ② 结束索引(不包含) |
否 |
splice() |
数组(同时)插入元素、删除元素 |
被删除元素组成的数组 |
① 开始索引 ② 要删除个数 ③ 插入元素 1 ④ 插入元素 2 |
是 |
搜索和位置方法
- ECMASctipr 提供 2 类搜索数组的方法:严格相等搜索和断言函数搜索
严格相等
- 3 个方法:
indexOf()
、lastIndexOf()
、includes()
- 均接收 2 个参数:要查找的元素(必填)、起始搜索位置(非必填)
indexOf()
和includes()
从前向后搜索,lastIndexOf()
从后向前搜索
indexOf()
、lastIndexOf()
返回要查找元素首次出现的位置(无匹配返回-1),includes()
返回布尔值
- 查找时,均使用
===
全等比较
includes()
是 ES7 新增的方法
let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]
console.log(numbers.indexOf(4))
console.log(numbers.lastIndexOf(4))
console.log(numbers.includes(4))
console.log(numbers.indexOf(4, 4))
console.log(numbers.lastIndexOf(4, 4))
console.log(numbers.includes(4, 7))
let man = { name: 'Nicholas' }
let human = [{ name: 'Nicholas' }]
let human2 = [man]
console.log(human.indexOf(man))
console.log(human2.indexOf(man))
console.log(human.includes(man))
console.log(human2.includes(man))
严格相等 |
参数 |
查找顺序 |
返回 |
indexOf() |
① 要查找元素(必) ② 起始搜索位置(非) |
前 → 后 |
要查找元素首次出现的位置,无匹配返回-1 |
lastIndexOf() |
① 要查找元素(必) ② 起始搜索位置(非) |
后 → 前 |
要查找元素首次出现的位置,无匹配返回-1 |
includes() |
① 要查找元素(必) ② 起始搜索位置(非) |
前 → 后 |
布尔值 |
断言函数
- 2 个方法:
find()
和findIndex()
- 均接收 2 个参数:断言函数、指定断言函数内部 this 的值(可选)
- 断言函数又接收 3 个参数:元素、索引、数组本身
find()
返回第一个匹配的元素,无匹配返回 undefined
findIndex()
返回第一个匹配元素的索引,无匹配返回-1
people = [
{ name: 'Matt', age: 27 },
{ name: 'Nicholas', age: 29 },
]
console.log(people.find((e, i, arr) => e.age > 28))
console.log(people.findIndex((e, i, arr) => e.age > 28))
console.log(people.find((e, i, arr) => e.age > 30))
console.log(people.findIndex((e, i, arr) => e.age > 30))
numbers = [3, 6, 9]
numbers.find((e, i, arr) => {
console.log(e)
console.log(i)
console.log(arr)
return e % 2 === 0
})
断言函数 |
参数 |
返回 |
find() |
① 断言函数(必) ② 指定断言函数内部 this 的值(非) |
第一个匹配的元素,无匹配返回 undefined |
findIndex() |
① 断言函数(必) ② 指定断言函数内部 this 的值(非) |
第一个匹配元素的索引,无匹配返回-1 |
迭代方法
- 5 个方法
every()
、some()
、filter()
、map()
、forEach()
- 每个方法接收 2 个参数:以每一项为参数运行的函数、作为函数运行上下文的作用域对象(非必填,影响函数中 this 的值)
- 传给每个方法的函数接收 3 个参数:元素、索引、数组本身
every()
:对数组每一项运行传入的函数,若每项都返回 true,则该方法返回 true
some()
:对数组每一项运行传入的函数,若有一项返回 true,则该方法返回 true
filter()
:对数组每一项运行传入的函数,由返回 true 的项组成数组,返回给该方法
map()
:对数组每一项运行传入的函数,由每项调用的结果组成数组,返回给该方法
forEach()
:对数组每一项运行传入的函数,相当于使用 for 循环遍历数组,该方法没有返回值
- 除了
forEach()
,其他 4 种方法的参数运行函数都必须有 return(或取消大括号的箭头函数)
numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let everyResult = numbers.every((item, index, array) => item > 2)
console.log(everyResult)
let someResult = numbers.some((item, index, array) => item > 2)
console.log(someResult)
let filterResult = numbers.filter((item, index, array) => item > 2)
console.log(filterResult)
let mapResult = numbers.map((item, index, array) => item * 2)
console.log(mapResult)
let forEachResult = numbers.forEach((item, index, array) => {
item = item * 2
})
console.log(forEachResult)
numbers.forEach((item, index, array) => {
console.log(item * 2)
})
迭代方法 |
参数 |
返回 |
every() |
① 运行函数(必),必须有返回值 ② 函数的作用域对象(非) |
每项运行参数函数,每项都返回 true 方法才返回 true |
some() |
① 运行函数(必),必须有返回值 ② 函数的作用域对象(非) |
每项运行参数函数,有一项返回 true 方法就返回 true |
filter() |
① 运行函数(必),必须有返回值 ② 函数的作用域对象(非) |
每项运行参数函数,返回 由返回 true 的项组成的数组 |
map() |
① 运行函数(必),必须有返回值 ② 函数的作用域对象(非) |
每项运行参数函数,返回 由每项调用的结果组成的数组 |
forEach() |
① 运行函数(必),没有返回值 ② 函数的作用域对象(非) |
无返回值 |
归并方法
- 2 个方法
reduce()
和reduceRight()
,均迭代数组的所有项,在此基础上构建一个最终返回值
- 每个方法接收 2 个参数:对每一项都会运行的归并函数、归并起点的初始值(非必填)
- 每个归并函数接收 4 个参数:上一个归并值、当前项、当前索引、数组本身
- 若省略归并起点值,则首次迭代将从数组第 2 项开始,传给归并函数的第 1 个参数是数组第 1 项,第 2 个参数是数组第 2 项
reduce()
从前向后遍历,reduceRight()
反之
values = [1, 2, 3, 4, 5]
let sum1 = values.reduce((pre, cur, index, arr) => {
console.log(pre, cur, index)
return pre + cur
})
console.log(sum1)
let sum2 = values.reduce((pre, cur, index, arr) => {
console.log(pre, cur, index)
return pre + cur
}, 10)
console.log(sum2)
let sum3 = values.reduceRight((pre, cur, index, arr) => {
console.log(pre, cur, index)
return pre + cur
})
console.log(sum3)
let sum4 = values.reduceRight((pre, cur, index, arr) => {
console.log(pre, cur, index)
return pre + cur
}, 10)
console.log(sum4)
迭代方法 |
参数 |
遍历方向 |
返回 |
reduce() |
① 归并函数(必) ② 归并起点值(非) |
前 → 后 |
遍历后最终返回值 |
reduceRight() |
① 运行函数(必) ② 归并起点值(非) |
后 → 前 |
遍历后最终返回值 |
总结 & 问点
- 有哪些基本方法可以创建数组?如何将类数组或一组参数转换为数组实例?
- 为什么不建议使用数组空位?如果确实需要呢?
- 数组的 length 属性是可变的么?如何快速的向数组末尾添加元素?
- 如何判断一个对象是不是数组?
- 用什么方法获取数组索引组成的数组?元素和键值对呢?
- 用什么方法批量填充数组的部分内容?用什么方法获取数组每个值拼接的字符串?若指定分隔符呢?
- 数组的“栈方法”和“队列方法”分别是怎样的用法和返回值?
- 如何按照升序/降序排列数组元素?如果数组元素是数值,如何简化写法?
- 如何将数组和类数组对象打平后添加到另一个数组的末尾?不打平添加呢?
- 请详述 slice()和 splice()的含义、用法、返回值及是否改变原数组
- 有哪些方法可以搜索数组?请分别详述其用法并举例
- 有哪些数组迭代方法?请分别详述其用法并举例
- 有哪些数组归并方法?请分别详述其用法并举例