JavaScript 解密 —— 数组(Array)及其函数式操作

一、创建数组

1
2
3
4
5
6
7
8
9
10
11
const ninjas = ["Kuma", "Hattori", "Yagyu"]
const samurai = new Array("Oda", "Tomoe")

console.log(ninjas.length) // 3
console.log(ninjas[ninjas.length-1]) // Yagyu
console.log(ninjas[4]) // undefined
ninjas[4] = "Ishi"
console.log(ninjas.length) // 5
console.log(ninjas) // [ 'Kuma', 'Hattori', 'Yagyu', <1 empty item>, 'Ishi' ]
ninjas.length = 2
console.log(ninjas) // [ 'Kuma', 'Hattori' ]
  • 数组可以使用 [] 字面量或 Array() 构造器创建
  • 可以使用索引访问数组中的元素。索引从 0array.length - 1
  • 数组的 length 属性表示数组的大小(即元素的个数)。读取超出索引范围的元素,会得到 undefined
  • 给超出索引范围的元素赋值,会自动扩展数组
  • 将数组的 length 属性改为一个较小的值,会自动删除溢出的元素

array

二、添加和移除元素

  • push:向数组尾部添加元素
  • unshift:向数组头部添加元素
  • pop:从数组尾部弹出元素
  • shift:从数组头部弹出元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const ninjas = []
ninjas.push("Kuma")
ninjas.push("Hattori")
console.log(ninjas) // [ 'Kuma', 'Hattori' ]

ninjas.unshift("Yagyu")
console.log(ninjas) // [ 'Yagyu', 'Kuma', 'Hattori' ]

const lastNinja = ninjas.pop()
console.log(lastNinja) // Hattori
console.log(ninjas) // [ 'Yagyu', 'Kuma' ]

const firstNinja = ninjas.shift()
console.log(firstNinja) // Yagyu
console.log(ninjas) // [ 'Kuma' ]

push & shift

从任意位置添加或移除元素

delete

1
2
3
4
const ninjas = ["Yagyu", "Kuma", "Hattori", "Fuma"]
delete ninjas[1]
console.log(ninjas) // [ 'Yagyu', <1 empty item>, 'Hattori', 'Fuma' ]
console.log(ninjas.length) // 4

delete 删除数组中的元素,实际上等于用 undefined 替换掉了删除的元素,导致数组中的对应位置出现一个“洞”(undefined),数组仍保持原来的长度。因此不符合最初的目的。
delete

splice

1
2
3
4
5
6
7
8
9
10
const ninjas = ["Yagyu", "Kuma", "Hattori", "Fuma"]
var removedItems = ninjas.splice(1, 1)
console.log(removedItems) // [ 'Kuma' ]
console.log(ninjas.length) // 3
console.log(ninjas) // [ 'Yagyu', 'Hattori', 'Fuma' ]

removedItems = ninjas.splice(1, 2, "Mochizuki", "Yoshi", "Momochi")
console.log(removedItems) // [ 'Hattori', 'Fuma' ]
console.log(ninjas) // [ 'Yagyu', 'Mochizuki', 'Yoshi', 'Momochi' ]
console.log(ninjas.length) // 4

移除元素时,splice 方法接收两个参数:被移除片段的起点的索引和片段的长度,返回移除的元素。

splice 方法还可用于在数组的指定位置插入元素。如上面代码中的 splice(1, 2, "Mochizuki", "Yoshi", "Momochi") 即表示从索引 1 开始移除 2 个元素,并在该位置处添加 "Mochizuki""Yoshi""Momochi" 三个元素。

三、数组的一般操作

遍历数组

for

1
2
3
4
5
6
7
const ninjas = ["Yagyu", "Kuma", "Hattori"]
for(let i = 0; i < ninjas.length; i++){
console.log(ninjas[i])
}
// Yagyu
// Kuma
// Hattori

forEach

1
2
3
4
5
6
7
8
const ninjas = ["Yagyu", "Kuma", "Hattori"]

ninjas.forEach(ninja => {
console.log(ninja)
})
// Yagyu
// Kuma
// Hattori

Mapping

forEach

1
2
3
4
5
6
7
8
9
10
11
12
13
const ninjas = [
{name: "Yagyu", weapon: "shuriken"},
{name: "Yoshi", weapon: "katana"},
{name: "Kuma", weapon: "wakizashi"}
]

const weapons = []
ninjas.forEach(ninja => {
weapons.push(ninja.weapon)
})

console.log(weapons)
// [ 'shuriken', 'katana', 'wakizashi' ]

map

1
2
3
4
5
6
7
8
9
const ninjas = [
{name: "Yagyu", weapon: "shuriken"},
{name: "Yoshi", weapon: "katana"},
{name: "Kuma", weapon: "wakizashi"}
]

const weapons = ninjas.map(ninja => ninja.weapon)
console.log(weapons)
// [ 'shuriken', 'katana', 'wakizashi' ]

map 将其 callback 函数(即 ninja => ninja.weapon)应用到数组的每一个元素上,并以 callback 函数的返回值 ninja.weapon 为元素创建一个新的数组作为 map 的返回值。
map

Testing

every

1
2
3
4
5
6
7
8
9
10
11
const ninjas = [
{name: "Yagyu", weapon: "shuriken"},
{name: "Yoshi" },
{name: "Kuma", weapon: "wakizashi"}
]

const allNinjasAreNamed = ninjas.every(ninja => "name" in ninja)
console.log(allNinjasAreNamed) // true

const allNinjasAreArmed = ninjas.every(ninja => "weapon" in ninja)
console.log(allNinjasAreArmed) // false

every 方法接收一个 callback 函数,将其应用到数组的每一个元素。若所有的 callback 执行后的返回值都为 true,则 every 返回 true;否则 every 返回 false。
every1

every2

some

1
2
3
4
5
6
7
8
9
10
11
const ninjas = [
{name: "Yagyu", weapon: "shuriken"},
{name: "Yoshi" },
{name: "Kuma", weapon: "wakizashi"}
]

const allNinjasAreNamed = ninjas.every(ninja => "name" in ninja)
console.log(allNinjasAreNamed) // true

const allNinjasAreArmed = ninjas.some(ninja => "weapon" in ninja)
console.log(allNinjasAreArmed) // true

some 方法接收一个 callback 函数,将其应用到数组的每一个元素。若至少有一个数组元素应用 callback 函数后返回 true,则 some 返回 true;否则 some 返回 false。

every 可以检查数组的所有元素是否都满足一个特定的条件,该条件由 callback 函数指定。而 some 用于检查是否至少有一个数组元素满足某个特定条件。

some

Searching

find

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const ninjas = [
{name: "Yagyu", weapon: "shuriken"},
{name: "Yoshi" },
{name: "Kuma", weapon: "wakizashi"}
]

const ninjaWithWakizashi = ninjas.find(ninja => {
return ninja.weapon === "wakizashi"
})
console.log(ninjaWithWakizashi)
// { name: 'Kuma', weapon: 'wakizashi' }

const ninjaWithKatana = ninjas.find(ninja => {
return ninja.weapon === "katana"
})
console.log(ninjaWithKatana)
// undefined

find 方法会以 callback 函数为规则搜索数组中的元素,返回满足条件(callback 返回 true)的第一个元素。若所有元素都不满足,则返回 undefined
find

filter

1
2
3
4
5
6
7
8
9
10
11
12
const ninjas = [
{name: "Yagyu", weapon: "shuriken"},
{name: "Yoshi" },
{name: "Kuma", weapon: "wakizashi"}
]

const armedNinjas = ninjas.filter(ninja => "weapon" in ninja)
console.log(armedNinjas)
// [
// { name: 'Yagyu', weapon: 'shuriken' },
// { name: 'Kuma', weapon: 'wakizashi' }
// ]

filter 方法则用来以 callback 函数为规则搜索多个数组元素,符合条件(callback 返回 true)的多个元素以新数组的形式返回。
filter

Finding index
1
2
3
4
5
6
7
const ninjas = ["Yagyu", "Yoshi", "Kuma", "Yoshi"]

console.log(ninjas.indexOf("Yoshi")) // 1
console.log(ninjas.lastIndexOf("Yoshi")) // 3

const yoshiIndex = ninjas.findIndex(ninja => ninja === "Yoshi")
console.log(yoshiIndex) // 1
Sorting

sort 方法的常见形式:array.sort((a, b) => a - b)

传递给 sort 的 callback 函数(如上面的 (a, b) => a - b)即为具体的排序规则,可能达到的效果如下:

  • callback 函数的返回值小于 0,则 a 排在 b 前面
  • callback 函数的返回值等于 0,则 ab 在排序上权重相同
  • callback 函数的返回值大于 0,则 a 排在 b 后面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const ninjas = [{name: "Yoshi"}, {name: "Hattori"}, {name: "Kuma"}]

ninjas.sort(function(ninja1, ninja2){
if(ninja1.name < ninja2.name) { return -1 }
if(ninja1.name > ninja2.name) { return 1 }
return 0
})

console.log(ninjas)
// [ { name: 'Hattori' }, { name: 'Kuma' }, { name: 'Yoshi' } ]

ninjas.sort((ninja1, ninja2) => ninja1.name.length - ninja2.name.length)
console.log(ninjas)
// [ { name: 'Kuma' }, { name: 'Yoshi' }, { name: 'Hattori' } ]
Aggregating

forEach

1
2
3
4
5
6
7
8
const numbers = [1, 2, 3, 4]
let sum = 0

numbers.forEach(number => {
sum += number
})

console.log(sum) // 10

reduce

1
2
3
4
const numbers = [1, 2, 3, 4]

const sum = numbers.reduce((aggregated, number) => aggregated + number, 0)
console.log(sum) // 10

reduce

参考资料

Secrets of the JavaScript Ninja, Second Edition