# 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];
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
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']
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]
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
}
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
2
3
4
5
6
7
8
9
10
11
- 需要注意,
forEach
循环遍历是没有办法通过break
和return
终止循环的。
使用 for...of
循环
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let item of numbers) {
console.log(item);
}
2
3
4
5
for (let item of numbers)
循环在变量的时候,会依次取出数组中的值赋值给 item(临时变量),for...of
不支持获取 index,如果要获取 index,需要自己在循环外部定义并自己进行处理;for...of
循环支持break
和return
中断循环。
# 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]
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]
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]
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]
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]
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]
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]
2
3
4
5
如果不指定参数,就相当于使用所有元素返回一个新的数组,也就是复制一个数组:
let arr = [1, 2, 3, 4];
let sub = arr.slice();
console.log(sub); // [1, 2, 3, 4]
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]
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"
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
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
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
2
3
4
# 6 includes()
.includes()
:可以判断数组是否包含某个元素,返回布尔值。
举个栗子:
const fruits = ["apple", "banana", "orange", "mango"];
console.log(fruits.includes("banana")); // true
console.log(fruits.includes("grape")); // false
2
3
4
还可以指定查找的其实位置:
const fruits = ["apple", "banana", "orange", "mango"];
console.log(fruits.includes("banana", 2)); // false,指定从index为2的位置查找
2
3
- 注意查找的时候是按照引用来查找的,两个对象属性相同,也是不同的。
includes()
比 indexOf()
牛比的是,它能识别 NaN
。
举个栗子:
const arr = [1, NaN, 3];
console.log(arr.includes(NaN)); // true
console.log(arr.indexOf(NaN)); // -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);
2
3
4
5
6
# 2 reverse()
.reverse()
:可以将数组中的元素顺序进行反转。
举个栗子:
let arr = [1, 2, 3, 4];
arr.reverse(); // 倒序
console.log(arr); // [4, 3, 2, 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]
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 } }
2
3
4
5
6
7
8
...
展开对象可以用来复制对象,注意是浅拷贝。展开运算符(...
)和Object.assign()
都不会复制原型链上的属性,它们仅复制对象自身的可枚举属性(Own Properties)。