JavaScript数组
数组简介和定义
什么是数组
数组(Array)是 JavaScript 中的一种引用数据类型,用于在一个变量中存储多个数据。
数组具有以下特点:
- 可以存储多个数据,且数据类型可以不同。
- 数组中的每个元素都有对应的下标(索引)。
- 下标从
0开始。 - 数组长度可以动态变化。
数组的定义方法
数组的定义方法有几种:
//使用 [] 定义数组,是最常用、最推荐的方式。var arr = ['A', 'B','C','D'];//使用 new Array() 创建数组var arr = new Array('A','B', 'C', 'D');//下面的代码表示定义一个长度为4的数组,但是这4项都是 undefinedvar arr = new Array(4);//根据传入的参数创建数组,解决了 Array() 传入单个数字时的歧义。const arr2 = Array.of(10, 20, 30); // [10, 20, 30]访问数组项
通过数组的下标访问元素,语法:
//数组名[下标]const fruits = ["苹果", "香蕉", "橘子"];
console.log(fruits[0]); // 苹果console.log(fruits[1]); // 香蕉console.log(fruits[2]); // 橘子下标越界
当访问的下标不存在时,JavaScript 通常返回 undefined,不会直接报错。
const arr = [10, 20, 30];
console.log(arr[3]); // undefinedconsole.log(arr[10]); // undefinedconsole.log(arr[-1]); // undefined注意:对数组本身访问不存在的下标通常不会报错,但继续访问 undefined 的属性可能报错。
数组的长度
使用 length 属性获取数组长度。
const arr = [10, 20, 30, 40];
console.log(arr.length); // 4
// 获取最后一个元素console.log(arr[arr.length - 1]); // 40length 可以修改:
const arr = [10, 20, 30];
arr.length = 2;console.log(arr); // [10, 20]
arr.length = 5; // 长度为5,后面增加3个空槽位注意:数组长度不一定等于实际存在的元素数量,因为 JavaScript 允许稀疏数组。
更改数组项
直接通过下标修改数组中的元素。
const arr = [10, 20, 30];arr[1] = 99;console.log(arr); // [10, 99, 30]也可以通过新的下标添加元素:
arr[3] = 40;
console.log(arr); // [10, 99, 30, 40]即使使用 const 定义数组,也可以修改内部元素,因为 const 限制的是变量重新赋值,并不禁止修改数组内容。
数组的遍历
数组遍历,就是依次访问数组中的元素。(在学习ES6的时候,有扩展很多循环语法,基础的记住for和while,数组使用for…of,对象使用for…in,特殊情况除外 )
方法1:for 循环:适合需要操作下标的场景。
const arr = [10, 20, 30];
for (let i = 0; i < arr.length; i++) { console.log(arr[i]);}方法2:for…of:适合只需要获取元素值的场景。
const arr = [10, 20, 30];
for (const item of arr) { console.log(item);}方法3:forEach():forEach() 可以同时获取元素值和下标,但不能像普通 for 循环一样通过 break 提前退出。
const arr = [10, 20, 30];
arr.forEach((item, index) => { console.log(index, item);});数组类型的检测
JavaScript 数组本质上属于对象,因此不能单纯使用 typeof 判断。
const arr = [10, 20, 30];
console.log(typeof arr); // object常见检测方式:
| 检测方式 | 示例 | 说明 |
|---|---|---|
Array.isArray() | Array.isArray(arr) | 推荐方式 |
instanceof | arr instanceof Array | 检查原型链,跨执行环境可能不准确 |
Object.prototype.toString.call() | Object.prototype.toString.call(arr) | 返回 [object Array] |
实际开发推荐:
const arr = [1, 2, 3];
console.log(Array.isArray(arr)); // trueconsole.log(Array.isArray({})); // falseconsole.log(Array.isArray("abc")); // false数组的常用方法
数组提供大量内置方法,主要包括添加、删除、查找、遍历、排序和转换。这里只列举一些常用的,在ES6语法中会再增加一些(slice、splice、split这三个英文非常像,初学很可能弄混,这三个是完全不一样功能的,split是字符串的方法,数组可以通过join转字符串,字符串可以使用split转为数组)
添加与删除:这5个是最基础的,splice是功能最多的,push和pop是开发中最常用的
| 方法 | 作用 | 是否修改原数组 |
|---|---|---|
push() | 末尾添加元素 | 是 |
pop() | 删除末尾元素 | 是 |
unshift() | 开头添加元素 | 是 |
shift() | 删除开头元素 | 是 |
splice() | 添加、删除或替换指定位置元素 | 是 |
push()方法:(末尾追加)
-
push()方法用来在数组末尾推入新项,参数就是要推入 的项
-
如果要推入多项,可以用逗号隔开
-
调用push()方法后,数组会立即改变,不需要赋值
var arr = [1, 2, 3];arr.push(4);console.log(arr); // [1, 2, 3, 4]arr.push(5, 6);console.log(arr); // [1, 2, 3, 4, 5, 6]
pop()方法:(删除末尾)
-
与push()相反,pop()方法用来删除数组中的最后一项
-
pop()方法不仅会删除数组末项,而且会返回被删除的项
var arr = [1, 2, 3, 4];var item = arr.pop();console.log(arr); // [1, 2, 3]console.log(item); // 4
unshift()方法:(头部插入)
-
unshift()方法用来在数组头尾插入新项,参数就是要 插入的项
-
如果要插入多项,可以用逗号隔开
-
调用unshift()方法后,数组会立即改变,不需要赋值
var arr = [2, 3, 4];arr.unshift(1);console.log(arr); // [1, 2, 3, 4]arr.unshift(-1, 0);console.log(arr); // [-1, 0, 1, 2, 3, 4]
shift()方法:(删除头部)
-
与unshift()相反,shift()方法用来删除数组中下标为0 的项
-
shift()方法不仅会删除数组末项,而且会返回被删除的项
var arr = [10, 20, 30];var item = arr.shift();console.log(arr); // [20, 30]console.log(item); // 10
splice()方法
-
splice()方法第一个参数是从哪个下标开始,第二个参数是删除多少个项
arr.splice(起始下标, 删除个数)var arr = ['A','B','C','D','E','F','G'];arr.splice(3,2,'X','Y','Z');console.log(arr);// 下标3开始,连续替换2项;这种说法错误的// 准确是:下标3开始,连续删除2项,然后插入新元素// [ 'A', 'B', 'C', 'X', 'Y', 'Z','F', 'G'] -
splice()方法可以用于在指定位置插入新项(第二个参数设置0,就没有删除项)
arr.splice(起始下标, 0, 新元素)var arr = [1, 2, 5, 6];arr.splice(2, 0, 3, 4);console.log(arr); // [1, 2, 3, 4, 5, 6] -
splice()方法可以用于删除指定项;result返回被删除的项
arr.splice(起始下标, 删除个数)var arr = [10, 20, 30, 40];var result = arr.splice(1, 1);console.log(arr); // [10, 30, 40]console.log(result); // [20] -
splice()方法会以数组形式返回被删除的项
查找与判断:
| 方法 | 作用 | 返回值 |
|---|---|---|
indexOf() | 查找元素首次出现的位置 | 下标或 -1 |
lastIndexOf() | 查找元素最后出现的位置 | 下标或 -1 |
includes() | 判断是否包含元素 | 布尔值 |
find() | 查找第一个符合条件的元素 | 元素或 undefined |
findIndex() | 查找第一个符合条件的元素下标 | 下标或 -1 |
some() | 是否至少一个元素符合条件 | 布尔值 |
every() | 是否所有元素符合条件 | 布尔值 |
indexOf()方法的功能是搜索数组中的元素,并返回它所 在的位置,如果元素不存在,则返回-1
var arr = ['A', 'B', 'C', 'D'];
console.log(arr.indexOf('B')); // 1console.log(arr.indexOf('Z')); // -1includes()方法的功能是判断一个数组是否包含一个指定 的值,返回布尔值
var arr = ['A', 'B', 'C'];console.log(arr.includes('B')); // trueconsole.log(arr.includes('Z')); // false截取、合并与转换
| 方法 | 作用 | 是否修改原数组 |
|---|---|---|
slice() | 截取数组片段 | 否 |
concat() | 合并数组 | 否 |
join() | 将数组连接成字符串 | 否 |
reverse() | 反转数组 | 是 |
sort() | 对数组排序 | 是 |
flat() | 展平嵌套数组 | 否 |
slice()方法:slice()方法用于得到子数组,slice(a, b)截取的子数组从下标为a的项开始,到下标为 b(但不包括下标为b的项)结束
-
标准写法
var arr = ['A','B','C','D','E','F'];var result = arr.slice(1, 4);console.log(result); // ['B','C','D'] 从下标 1 开始;(B)到下标 4 结束(不包含4)所以是:B、C、Dconsole.log(arr); // ['A','B','C','D','E','F'] -
slice()如果不提供第二个参数,则表示从指定项开始,提 取所有后续所有项作为子数组
var arr = ['A','B','C','D','E','F'];var result = arr.slice(2);console.log(result); // ['C','D','E','F'] -
slice(a, b)方法不会更改原有数组,返回的是截取的数组
var arr = [1, 2, 3, 4];var result = arr.slice(1, 3);console.log(result); // [2, 3]console.log(arr); // [1, 2, 3, 4] ✔ 原数组不变 -
slice()方法的参数允许为负数,表示数组的倒数第几项
var arr = ['A','B','C','D','E','F'];var result = arr.slice(-3);console.log(result); // ['D','E','F']
concat()方法
-
concat()方法可以合并连结多个数组
-
concat()方法不会改变原数组
var arr1 = [1, 2, 3, 4];var arr2 = [5, 6, 7, 8];var arr3 = [9, 10, 11];var arr = arr1.concat(arr2 , arr3);console.log(arr);// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
join()方法
-
数组的join()方法可以使数组转为字符串;字符串的split() 方法可以使字符串转为数组。
-
join()的参数表示以什么字符作为连接符,如果留空则默 认以逗号分隔,如同调用toString()方法
var arr = ['A', 'B', 'C'];var str = arr.join('-');console.log(str); // "A-B-C" -
split()的参数表示以什么字符拆分字符串,一般不能留空
var str = "A-B-C";var arr = str.split('-');console.log(arr); // ["A", "B", "C"]
reverse()方法
-
reverse()方法用来将一个数组中的全部项顺序置反
var arr = ["A","B","C","D"];arr.reverse();console.log(arr); // ["D","C","B","A"]
遍历相关算法
-
题目:求数组中每一项的总和、平均数
var arr = [10, 20, 30, 40, 50];var sum = 0;for (var i = 0; i < arr.length; i++) {sum += arr[i];}var avg = sum / arr.length;console.log("总和:" + sum);console.log("平均数:" + avg);var arr = [10, 20, 30, 40, 50];var sum = arr.reduce(function (acc, cur) {return acc + cur;}, 0);var avg = sum / arr.length;console.log(sum, avg); -
题目:求数组项的最大值和最小值
var arr = [12, 5, 78, 23, 9, 100, 34];// 假设第一个是最大值和最小值var max = arr[0];var min = arr[0];for (var i = 1; i < arr.length; i++) {if (arr[i] > max) {max = arr[i];}if (arr[i] < min) {min = arr[i];}}console.log("最大值:" + max);console.log("最小值:" + min); -
题目:统计符合条件的元素数量
const arr = [12, 25, 36, 47, 58];let count = 0;for (const item of arr) {if (item % 2 === 0) {count++;}}console.log(count); // 3
数组去重和随机样本
数组去重
-
题目:去掉数组中的重复项
-
思路:准备一个空结果数组,遍历原数组,如果遍历到的项不在结果数组中,则推入结果数组。
var arr = [1, 2, 3, 2, 4, 1, 5];var result = [];for (var i = 0; i < arr.length; i++) {if (!result.includes(arr[i])) {result.push(arr[i]);}}console.log(result);
ES6版本:使用Set,Set 是一种不保存重复值的集合类型,配合展开运算符 ... 可以转换回数组。
const arr = [1, 2, 2, 3, 3, 4];
const result = [...new Set(arr)];
console.log(result); // [1, 2, 3, 4]filter() + indexOf():原理:只保留每个元素首次出现的位置。这种方式对 NaN 等特殊值不如 Set 合适。
const arr = [1, 2, 2, 3, 3, 4];
const result = arr.filter((item, index) => { return arr.indexOf(item) === index;});
console.log(result); // [1, 2, 3, 4]随机样本
-
题目:请随机从原数组中取3项
-
思路:准备一个空结果数组,遍历原数组,随机选择一项 ,推入结果数组,并且将这项删除
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];var result = [];for (var i = 0; i < 3; i++) {// 1. 随机下标var index = Math.floor(Math.random() * arr.length);// 2. 取出该项result.push(arr[index]);// 3. 删除该项(防止重复)arr.splice(index, 1);}console.log(result);
冒泡排序(重点)
冒泡排序(Bubble Sort)是一种基础排序算法,**核心思想:相邻两个元素进行比较,如果顺序错误就交换位置。**以升序排序为例,每一轮都把当前未排序部分的最大元素移动到右边。
xxxxxxxxxx var arr = [];for (var i = 0; i < 5; i++) {(function (i) { arr.push(function () { console.log(i); });})(i);//这是立即执行函数,i是当前循环的变量}arr2; // 弹出2js
const arr = [5, 3, 8, 4, 2];
for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { const temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } }}
console.log(arr); // [2, 3, 4, 5, 8]代码理解:
| 代码 | 作用 |
|---|---|
外层 i 循环 | 控制排序轮数 |
内层 j 循环 | 比较相邻元素 |
arr[j] > arr[j + 1] | 判断是否需要交换 |
temp | 临时保存元素,完成交换 |
arr.length - 1 - i | 排除已经排好序的末尾部分 |
实际开发中通常使用 sort() 排序,学习冒泡排序的重点在于掌握双层循环、相邻比较和元素交换。
二维数组
二维数组可以理解为数组中的元素仍然是数组。
访问二维数组:通过两个下标访问元素:
const arr = [ [1, 2, 3], [4, 5, 6], [7, 8, 9]];//第一个下标表示外层数组的索引,第二个表示内层数组的索引。console.log(arr[0][0]); // 1console.log(arr[1][2]); // 6console.log(arr[2][1]); // 8遍历二维数组<使用双层循环>使用双层循环>:
for (let i = 0; i < arr.length; i++) { for (let j = 0; j < arr[i].length; j++) { console.log(arr[i][j]); }}创建二维数组
例如创建一个 3 行 4 列,所有元素为 0 的二维数组:
const arr = Array.from( { length: 3 }, () => Array(4).fill(0));
console.log(arr);得到:
[ [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]注意:不要使用 Array(3).fill(Array(4).fill(0)) 创建需要独立修改每一行的二维数组,因为这种方式会让每一行引用同一个内层数组。
基本类型值和引用类型值
- 基本类型:number、boolean、string、undefined、 null
- 引用类型:array、object、function、regexp…
相等判断时的区别:
- 基本类型进行相等判断时,会比较值是否相等
- 引用类型进行相等判断时,会比较址是否相等,也就是说 它会比较是否为内存中的同一个东西
| 类型 | 举例 | 当 var a = b 变量传值时 | 当用 == 比较时 |
|---|---|---|---|
| 基本类型值 | 数字型、字符串型、布尔型、undefined 型 | 内存中产生新的副本 | 比较值是否相等 |
| 引用类型值 | 对象、数组 | 内存中不产生新的副本,而是让新变量指向同一个对象 | 比较内存地址是否相同,即比较是否是同一个对象 |
var a = 1;var b = a;var a = 2;// a 和 b 是不同的变量console.log(a);//2console.log(b);//1深克隆和浅克隆(重点)
-
浅克隆:只克隆数组的第一层,如果是多维数组,或者数 组中的项是其他引用类型值,则不克隆其他层
var arr = [1, 2, 3];var newArr = [];for (var i = 0; i < arr.length; i++) {newArr.push(arr[i]);}arr.push(222);console.log(newArr); // [1, 2, 3]console.log(arr); // [1, 2, 3,222]var arr = [{ a: 1 }, { b: 2 }];var newArr = [];for (var i = 0; i < arr.length; i++) {newArr.push(arr[i]);}newArr[0].a = 100//改变新数组,旧数组也更着变了console.log(newArr); // [ { a: 100 }, { b: 2 } ]console.log(arr); // [ { a: 100 }, { b: 2 } ] -
深克隆:克隆数组的所有层,要使用递归技术,在后面介绍,但是在现代开发中有一个api可以实现,不用手写递归,那就是
structuredClone(),自 2022 年 3 月起已在主流浏览器中广泛支持,Node.js 从 17.0.0 开始提供该 API。 -
它支持数组、普通对象、
Date、Map、Set等,也支持循环引用,但有一些限制。数据类型 是否支持 普通对象、数组 ✔ 嵌套对象、二维数组 ✔ Date、Map、Set ✔ 循环引用 ✔ 函数 ❌ DOM 元素 ❌ Proxy 代理对象 ❌ 自定义类的原型与方法 不能完整保留 -
对象深克隆:
// 对象深克隆const obj1 = {name: "张三",age: 20,hobby: ["篮球", "足球"],address: {city: "北京"}};const obj2 = structuredClone(obj1);obj2.hobby[0] = "羽毛球";obj2.address.city = "上海";console.log(obj1.hobby); // ["篮球", "足球"]console.log(obj1.address.city); // 北京 -
数组深克隆
const arr1 = [[1, 2], [3, 4]];const arr2 = structuredClone(arr1);arr2[0][0] = 99;console.log(arr1); // [[1, 2], [3, 4]]console.log(arr2); // [[99, 2], [3, 4]]
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














