# JavaScript教程 - 10 数组

前面我们保存数据的时候,是将数据保存在变量中,如果要保存2个数据,就声明2个变量。或者保存对象的信息,那就需要定义2个属性。

如果要保存100个数据呢,1000个数据......呢?

所以就需要保存数据的集合来保存批量的数据,下面介绍一下数组。

数组就是用来存储多个数据的集合,数组中的数据按照顺序排列的,可以通过索引来访问数组中的元素,索引从0开始。

# 10.1 数组的基础使用

# 10.1.1 创建数组

同样,数组和变量一样,在使用之前也要进行声明和初始化,数组变量是引用类型的

// 创建一个空数组
let array = new Array();
// 创建一个空数组
let numbers = [];

// 创建一个数组,数组的长度为5
let array = new Array(5);

// 创建一个数组,包含一个元素,值为5
let numbers = [5];
// 创建一个数组,其中包含一个元素,值为'5'
let array = new Array('5');
// 创建一个数组,并添加了5个元素
let arra2 = new Array(1, 3, 5, 7, 9);
// 创建了一个数组,并添加了5个元素
let fruits = ['Apple', 'Banana', 'Orange', 2, 5];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 在创建数组,可以使用 new Array() 或者 [] 的方式来定义。可以创建空数组,也可以创建指定长度的数组,还可以在创建的时候初始化数组中的元素。
  • 数组中的元素,数据类型可以是任意的,包括对象和函数,但是尽量让各个元素的数据类型一样,好操作,免得出错。

# 10.1.2 基础操作

# 1 获取长度

使用 .length 属性可以获取到数组的长度。

let numbers = [1, 2, 3, 4, 5];
console.log(numbers);  // [1, 2, 3, 4, 5]
console.log("数组长度:" + numbers.length);    // 输出:5
1
2
3

# 2 访问元素

访问数组元素是使用下标 index 来访问的,从0开始,所以最后一个元素的索引为 数组的长度 - 1

let numbers = [1, 2, 3, 4, 5];

let firstNum = numbers[0]; // 获取第一个元素
console.log(firstNum); // 输出:1

let lastNum = numbers[numbers.length - 1]; // 获取最后一个元素
console.log(lastNum); // 输出:5

// 修改元素
numbers[0] = 100; // 修改第一个元素的值为100
console.log(numbers[0]); // 100

numbers[2] = 50; // 修改第三个元素的值为50
console.log(numbers[2]); // 50

numbers[numbers.length] = 'Niubi';  // 此时已经越界了,相当于添加数据
console.log(numbers.length);  // 此时数组长度为6了
console.log(numbers);  // [100, 2, 50, 4, 5, 'Niubi']

numbers[100] = 'Doubi';  // 修改下标为100的元素
console.log(numbers.length);  // 此时数组长度为101了
console.log(numbers);  // [100, 2, 50, 4, 5, 'Niubi', 空属性 × 94, 'Doubi']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • 和其他语言不通, JS 中访问数组中的元素不会存在下标越界的问题,如果访问的下标大于等于数组长度,返回的元素数据为 undefined
  • 如果按照下标修改元素,如果下标大于等于数组长度,修改后,数组长度也会发生变化,会变为 下标 + 1 ,新增出来的元素为空,如果按照索引来获取这些元素,返回的值为 undefined
  • 通过 numbers[numbers.length] 这种方式赋值,其实就相当于在数组最后面添加元素

在 JS 中,还有更变态的,还可以直接修改数组的长度:

let numbers = [1, 2, 3, 4, 5];
numbers.length = 10;

console.log(numbers.length);  // 10
console.log(numbers);  // [1, 2, 3, 4, 5, 空属性 × 5]
console.log(numbers[7]); // undefined

numbers.length = 3;
console.log(numbers);  // [1, 2, 3]
1
2
3
4
5
6
7
8
9
  • 如果是扩大数组的长度,那么后面的元素为空,获取就是 undefined,虽然获取元素的值为 undefined,但是和直接给元素赋值为 undefined 还是有区别的,遍历数组的时候再讲;
  • 如果是缩小数组的长度, 相当于把后面的元素删掉。

# 10.1.3 遍历数组

遍历就是依次访问数组中的每一个元素。

使用 for 循环遍历

let numbers = new Array(10);  // 创建一个长度为10的数组
for (let i = 0; i < numbers.length; i++) {
  numbers[i] = i;  // 为每个元素赋值
}
console.log(numbers);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// 遍历进行输出
for (let i = 0; i < numbers.length; i++) {
  console.log(numbers[i]); // 依次输出: 0 1 2 3 4 5 6 7 8 9
}
1
2
3
4
5
6
7
8
9
10
  • 使用 for 循环,可以对数据进行遍历赋值和访问,注意判断结束的条件是: i < numbers.length

使用 forEach 循环遍历

let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(numbers); [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

numbers.forEach((item, index) => {
  console.log(`索引 ${index}:`, item);  // 索引 0: 0
});

// 索引 0: 0
// 索引 1: 1
// ...
// 索引 9: 9
1
2
3
4
5
6
7
8
9
10
11
  • 需要注意,forEach 循环遍历是没有办法通过 breakreturn 终止循环的。

使用 for...of 循环

let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (let item of numbers) {
  console.log(item);
}
1
2
3
4
5
  • for (let item of numbers) 循环在变量的时候,会依次取出数组中的值赋值给 item(临时变量), for...of 不支持获取 index,如果要获取 index,需要自己在循环外部定义并自己进行处理;
  • for...of 循环支持 breakreturn 中断循环。

# 10.1.4 数组元素的默认值

动态初始化方式定义的数组,例如 new int[5] ,没有设置数组中元素的值,元素是有默认值的。对于基本数据类型,byte、short、int、long、char 类型的数组,默认值为 0,float、double 类型的数组,默认值为 0.0,boolean 类型数组,默认值为 false。对于引用类型的数组,元素的默认值都是 null

举个栗子:

int [] numbers = new int[3];
console.log(Arrays.toString(numbers));       // [0, 0, 0]

boolean [] bools = new boolean[3];
console.log(Arrays.toString(bools));         // [false, false, false]

String [] strs = new String[3];
console.log(Arrays.toString(strs));          // [null, null, null]
1
2
3
4
5
6
7
8

Arrays.toString() 方法可以以字符串的形式打印数组中的内容。

# 10.2 数组常用的方法

下面介绍一下数组常用的方法,了解一下,在实际时候的时候,可以问一下大模型要实现的功能,看怎么实现。

# 10.2.1 修改数组内容的方法

下面的方法会修改数组的数据内容。

# 1 push()

.push() :在数组末尾添加元素。

举个栗子:

let arr = [1, 2];
let length = arr.push(3); 

console.log(length); // 返回新长度 → 3 
console.log(arr); // [1, 2, 3]
1
2
3
4
5

# 2 pop()

.pop() :删除并返回最后一个元素。

举个栗子:

let arr = [1, 2, 3];
let last = arr.pop(); // 返回 3

console.log(last); // 3
console.log(arr); // [1, 2]
1
2
3
4
5

# 3 unshift()

.unshift() :在开头添加元素。

举个栗子:

let arr = [1, 2];
let length = arr.unshift(0);   // 在开头添加0元素

console.log(length); // 返回新长度 → 3
console.log(arr); // [0, 1, 2]
1
2
3
4
5

# 4 shift()

.shift() :删除并返回第一个元素。

举个栗子:

let arr = [1, 2, 3];
let first = arr.shift(); // 删除并返回第一个元素

console.log(first);  // 1
console.log(arr); // [2, 3]
1
2
3
4
5

# 5 splice()

.splice(start, deleteCount, item1, item2, ...) :我们也可以使用这个方法删除元素,也可以删除后并插入新元素。

举个栗子:

let arr = [1, 2, 3, 4];
let deleteArray = arr.splice(1, 2, "a", "b"); // 从索引1删除2个元素,然后插入 "a", "b",返回的是被删除的元素
console.log(deleteArray);  // 被删除的元素[2, 3]
console.log(arr);  // [1, 'a', 'b', 4]

// 单纯的删除元素
arr.splice(1, 2);  // 从索引1删除2个元素
console.log(arr);  // [1, 4]
1
2
3
4
5
6
7
8

# 10.2.2 不改变原数组的方法

下面的方法不会改变原有数组,方法会返回一个新的数组。

# 1 slice()

.slice(start, end) :从 start 索引(包括)开始到 end 索引(不包括)结束,返回一个子数组。

别和splice()搞混了!

举个栗子:

let arr = [1, 2, 3, 4];
let sub = arr.slice(1, 3);

console.log(arr);  // 没变,还是[1, 2, 3, 4]
console.log(sub);  // [2, 3]
1
2
3
4
5

如果不指定参数,就相当于使用所有元素返回一个新的数组,也就是复制一个数组:

let arr = [1, 2, 3, 4];
let sub = arr.slice();

console.log(sub); // [1, 2, 3, 4]
1
2
3
4
  • 但是需要注意,slice()浅拷贝,关于浅拷贝和深拷贝,查看下面的内容。

# 2 concat()

.concat() :合并两个数组,得到一个新的数组。

举个栗子:

let a = [1, 2];
let b = [3, 4];
let c = a.concat(b); 

console.log(c);  // [1, 2, 3, 4]
1
2
3
4
5

# 3 join()

.join(separator) :将数组转换为字符串,使用 separator 连接。

举个栗子:

let arr = [1, 2, 3, 4];
let str = arr.join("-"); 

console.log(str);  // "1-2-3-4"
1
2
3
4

# 4 indexOf()

.indexOf() :从数组开头向末尾搜索,返回第一个匹配元素的索引,找不到则返回 -1

举个栗子:

let arr = [1, 2, 3, 4, 4, 3, 2, 1];
let index = arr.indexOf(2);

console.log(index);  // 找到第一个2的位置为:1
1
2
3
4

还可以指定查询的其实位置:

let arr = [1, 2, 3, 4, 4, 3, 2, 1];
let index = arr.indexOf(2, 3);  // 从index为3的位置开始查找

console.log(index); // 找到2的位置为:6
1
2
3
4

# 5 lastIndexOf()

.lastIndexOf() :从数组末尾向开头搜索,返回最后一个匹配元素的索引,找不到则返回 -1

举个栗子:

let arr = [1, 2, 3, 4, 4, 3, 2, 1];
let index = arr.lastIndexOf(2);

console.log(index);  // 找到最后个2的位置为:6
1
2
3
4

# 6 includes()

.includes() :可以判断数组是否包含某个元素,返回布尔值。

举个栗子:

const fruits = ["apple", "banana", "orange", "mango"];

console.log(fruits.includes("banana")); // true
console.log(fruits.includes("grape")); // false
1
2
3
4

还可以指定查找的其实位置:

const fruits = ["apple", "banana", "orange", "mango"];

console.log(fruits.includes("banana", 2)); // false,指定从index为2的位置查找
1
2
3
  • 注意查找的时候是按照引用来查找的,两个对象属性相同,也是不同的。

includes()indexOf() 牛比的是,它能识别 NaN

举个栗子:

const arr = [1, NaN, 3];

console.log(arr.includes(NaN));   // true
console.log(arr.indexOf(NaN));   // -1 (无法找到)
1
2
3
4

# 10.2.3 其他常用方法

# 1 sort()

.sort() :对数组进行排序,默认按字符串排序。

举个栗子:

let arr = [10, 2, 30];
arr.sort();   // 排序
console.log(arr);  // [10, 2, 30] → ["10", "2", "30"] → 按字符排序

arr.sort((a, b) => a - b); // 排序,可以传递一个函数,按照数字比较两个元素的大小,进行排序,实现升序或者降序
console.log(arr);
1
2
3
4
5
6

# 2 reverse()

.reverse() :可以将数组中的元素顺序进行反转。

举个栗子:

let arr = [1, 2, 3, 4];
arr.reverse();  // 倒序
console.log(arr);  // [4, 3, 2, 1]
1
2
3

# 10.3 数组的其他操作

# 10.3.1 展开数组

JavaScript的展开运算符是ES6引入的语法糖,使用三个连续的点(...)表示,主要用于展开可迭代对象(如数组或对象)中的元素或属性。

# 1 展开数组

可以将数组展开成单独的元素,用于创建新数组,也可以传递给函数作为参数。

let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; 

console.log(arr2); // [1, 2, 3, 4, 5]
1
2
3
4
  • ... 会展开数组的元素,可以作为元素创建一个新数组。
  • 需要注意,展开数组是浅拷贝。

# 2 展开对象

const original = { a: 1, b: { c: 2 } };
const copy = { ...original };

console.log(copy); // { a: 1, b: { c: 2 } }
copy.a = 10;       // 修改基本类型,不影响原对象
copy.b.c = 20;     // 修改嵌套对象,会影响原对象!

console.log(original); // { a: 1, b: { c: 20 } }
1
2
3
4
5
6
7
8
  • ... 展开对象可以用来复制对象,注意是浅拷贝。展开运算符(...)和 Object.assign() 都不会复制原型链上的属性,它们仅复制对象自身的可枚举属性(Own Properties)。