你是不是也遇到过这些问题:想往数组里加元素,分不清push和unshift;想排序,结果数字排得乱七八糟;想找个元素,写一堆循环还容易出错?其实JavaScript数组看似复杂,核心就那些方法。
一、先搞懂:JS数组到底是个啥?
和Java数组只能存同类型数据不同,JS数组是“万能收纳盒”——数字、字符串、对象、甚至另一个数组都能塞进去。它本质是特殊的对象,有个天生的属性length(长度),还有一堆现成的方法帮我们操作数据,不用自己写复杂逻辑。
二、数组的2种创建方式
1. 构造函数方式(new Array())
// 无参数:创建空数组
let arr1 = new Array();
console.log(arr1); // 输出:[]
console.log(arr1.length); // 输出:0
// 单个数字参数:创建指定长度的空数组(注意!不是存这个数字)
let arr2 = new Array(5);
console.log(arr2); // 输出:[empty × 5]
console.log(arr2.length); // 输出:5
// 多个参数:直接创建包含这些元素的数组
let arr3 = new Array(1, "hello", true);
console.log(arr3); // 输出:[1, "hello", true]
// 省略new关键字也能用(效果一样)
let arr4 = Array(2, 3);
console.log(arr4); // 输出:[2, 3]
2. 数组字面量([])【推荐用这个!】
// 空数组
let arr5 = [];
// 包含不同类型元素的数组(JS特色)
let arr6 = [10, "JS数组", {name: "张三"}, [1,2]];
console.log(arr6); // 输出:[10, "JS数组", {name: "张三"}, [1,2]]
console.log(arr6.length); // 输出:4
三、数组的增删改查(基础版)
1. 查/改:通过索引操作(索引从0开始!)
let arr = ["苹果", "香蕉", "橘子"];
// 查:获取索引1的元素
console.log(arr[1]); // 输出:香蕉
// 改:修改索引2的元素
arr[2] = "橙子";
console.log(arr); // 输出:["苹果", "香蕉", "橙子"]
// 增:直接给超出长度的索引赋值(中间会补空)
arr[5] = "葡萄";
console.log(arr); // 输出:["苹果", "香蕉", "橙子", empty × 2, "葡萄"]
console.log(arr.length); // 输出:6(长度自动更新)
2. 增:push()(末尾加)、unshift()(开头加)
let arr = ["张三", "李四"];
// push():末尾加1个或多个元素,返回新长度,改变原数组
let newLength1 = arr.push("王五", "赵六");
console.log(arr); // 输出:["张三", "李四", "王五", "赵六"]
console.log(newLength1); // 输出:4
// unshift():开头加1个或多个元素,返回新长度,改变原数组
let newLength2 = arr.unshift("小明");
console.log(arr); // 输出:["小明", "张三", "李四", "王五", "赵六"]
console.log(newLength2); // 输出:5
3. 删:pop()(删最后一个)、shift()(删第一个)
let arr = ["小明", "张三", "李四", "王五"];
// pop():删最后一个元素,返回被删的元素,改变原数组
let delItem1 = arr.pop();
console.log(arr); // 输出:["小明", "张三", "李四"]
console.log(delItem1); // 输出:王五
// shift():删第一个元素,返回被删的元素,改变原数组
let delItem2 = arr.shift();
console.log(arr); // 输出:["张三", "李四"]
console.log(delItem2); // 输出:小明
// 空数组调用pop/shift,返回undefined
let emptyArr = [];
console.log(emptyArr.pop()); // 输出:undefined
四、4种遍历数组的方式(必学!)
遍历就是“挨个访问数组里的元素”,不同场景用不同方式:
let fruits = ["苹果", "香蕉", "橙子"];
// 1. for循环(最基础,可控制遍历过程)
console.log("=== for循环 ===");
for (let i = 0; i < fruits.length; i++) {
// i是索引,fruits[i]是元素
console.log(`索引${i}:${fruits[i]}`);
}
// 输出:
// 索引0:苹果
// 索引1:香蕉
// 索引2:橙子
// 2. for...in(遍历索引,适合数组/对象)
console.log("=== for...in ===");
for (let index in fruits) {
console.log(`索引${index}:${fruits[index]}`);
}
// 输出和for循环一样
// 3. for...of(ES6,遍历元素,只适合数组/可遍历对象)
console.log("=== for...of ===");
for (let item of fruits) {
// 直接拿到元素,不用通过索引
console.log(item);
}
// 输出:苹果 香蕉 橙子
// 4. forEach(数组专属,更简洁,无返回值)
console.log("=== forEach ===");
fruits.forEach((item, index, arr) => {
// item:当前元素,index:索引,arr:原数组
console.log(`索引${index}:${item},原数组:${arr}`);
});
// 输出:
// 索引0:苹果,原数组:苹果,香蕉,橙子
// 索引1:香蕉,原数组:苹果,香蕉,橙子
// 索引2:橙子,原数组:苹果,香蕉,橙子
五、数组高级方法(提升效率的关键)
1. 不改变原数组的方法
(1)concat():数组合并
// 功能:合并多个数组/元素,返回新数组,原数组不变
let arr1 = [1, 2];
let arr2 = [3, 4];
// 合并arr1、arr2,再加一个元素5
let newArr = arr1.concat(arr2, 5);
console.log(newArr); // 输出:[1,2,3,4,5]
console.log(arr1); // 输出:[1,2](原数组没变)
(2)slice():数组截取(切片)
// 功能:截取指定范围的元素,返回新数组,原数组不变
// 语法:slice(start, end),包含start,不包含end
let arr = [10, 20, 30, 40, 50];
// 从索引1截取到索引3(不包含3)
let slice1 = arr.slice(1, 3);
console.log(slice1); // 输出:[20, 30]
// 只传start:从start截取到末尾
let slice2 = arr.slice(2);
console.log(slice2); // 输出:[30, 40, 50]
// 不传参数:复制整个数组(浅拷贝)
let slice3 = arr.slice();
console.log(slice3); // 输出:[10,20,30,40,50]
console.log(arr); // 输出:[10,20,30,40,50](原数组没变)
2. 改变原数组的方法
(1)reverse():数组反转
// 功能:颠倒数组元素顺序,返回反转后的原数组,改变原数组
let arr = [1,2,3];
let reversedArr = arr.reverse();
console.log(reversedArr); // 输出:[3,2,1]
console.log(arr); // 输出:[3,2,1](原数组变了)
(2)sort():数组排序【重点!】
// 注意:默认按Unicode码排序(数字会被转成字符串,坑!)
let numArr = [10, 2, 31, 4];
numArr.sort();
console.log(numArr); // 输出:[10, 2, 31, 4](不是我们要的数字排序)
// 正确排序:传回调函数 (a,b) => a-b(升序) / (a,b) => b-a(降序)
// 原理:回调返回正数→b排前面,负数→a排前面,0→顺序不变
numArr.sort((a, b) => a - b); // 升序
console.log(numArr); // 输出:[2,4,10,31]
numArr.sort((a, b) => b - a); // 降序
console.log(numArr); // 输出:[31,10,4,2]
// 字符串排序(按拼音/字母)
let nameArr = ["李四", "张三", "小明"];
nameArr.sort((a, b) => a.localeCompare(b)); // 中文升序
console.log(nameArr); // 输出:["李四", "小明", "张三"]
(3)splice():万能操作(删/增/替换)【重点!】
// 语法:splice(start, deleteCount, 添加的元素1, 添加的元素2...)
let arr = [1,2,3,4,5];
// 1. 删除:deleteCount>0,不添加元素
// 从索引2开始,删除2个元素
let delItems = arr.splice(2, 2);
console.log(delItems); // 输出:[3,4](返回被删的元素)
console.log(arr); // 输出:[1,2,5](原数组变了)
// 2. 添加:deleteCount=0,添加元素
// 从索引2开始,删0个,添加6、7
arr.splice(2, 0, 6, 7);
console.log(arr); // 输出:[1,2,6,7,5]
// 3. 替换:deleteCount>0,且添加元素
// 从索引3开始,删1个,替换成8
arr.splice(3, 1, 8);
console.log(arr); // 输出:[1,2,6,8,5]
3. 查找类方法(ES6新增)
(1)find():找第一个符合条件的元素
// 功能:返回第一个满足条件的元素,找不到返回undefined
let users = [
{id: 1, name: "张三", age: 20},
{id: 2, name: "李四", age: 25},
{id: 3, name: "王五", age: 25}
];
// 找第一个年龄等于25的用户
let targetUser = users.find((item, index) => {
// item:当前元素,index:索引
return item.age === 25;
});
console.log(targetUser); // 输出:{id: 2, name: "李四", age: 25}
// 找不存在的元素
let noUser = users.find(item => item.age === 30);
console.log(noUser); // 输出:undefined
(2)findIndex():找第一个符合条件的元素索引
// 功能:返回第一个满足条件的元素索引,找不到返回-1
let users = [
{id: 1, name: "张三", age: 20},
{id: 2, name: "李四", age: 25}
];
// 找年龄25的用户索引
let targetIndex = users.findIndex(item => item.age === 25);
console.log(targetIndex); // 输出:1
// 找不存在的元素索引
let noIndex = users.findIndex(item => item.age === 30);
console.log(noIndex); // 输出:-1
4. 转换类方法
(1)map():遍历数组生成新数组
// 功能:遍历每个元素,执行回调函数,返回新数组(原数组不变)
let nums = [1,2,3];
// 每个元素乘2
let doubleNums = nums.map((item) => {
return item * 2;
});
console.log(doubleNums); // 输出:[2,4,6]
console.log(nums); // 输出:[1,2,3](原数组不变)
// 实际场景:提取对象数组的某个属性
let users = [{name: "张三"}, {name: "李四"}];
let names = users.map(item => item.name);
console.log(names); // 输出:["张三", "李四"]
5. 归并类方法:reduce()(万能方法!)
// 功能:从左到右遍历数组,累加/合并成一个值,返回最终结果
// 语法:reduce((preTotal, value, index) => {}, 初始值)
// preTotal:上一次的结果,value:当前元素,index:索引
// 场景1:数组求和(带初始值0)
let nums = [1,2,3,4];
let sum = nums.reduce((pre, cur) => {
return pre + cur;
}, 0);
console.log(sum); // 输出:10
// 场景2:数组去重(常用!)
let arr = [1,2,2,3,3,3];
let uniqueArr = arr.reduce((pre, cur) => {
// 如果当前元素不在pre里,就加进去
if (!pre.includes(cur)) {
pre.push(cur);
}
return pre;
}, []); // 初始值是空数组
console.log(uniqueArr); // 输出:[1,2,3]
6. 判断类方法:every() / some()
let scores = [85, 90, 78, 95];
// every():所有元素满足条件才返回true,否则false
// 判断是否所有分数都≥80
let allPass = scores.every(item => item >= 80);
console.log(allPass); // 输出:false(78<80)
// some():只要有一个元素满足条件就返回true
// 判断是否有分数≥90
let hasExcellent = scores.some(item => item >= 90);
console.log(hasExcellent); // 输出:true
// 空数组特殊情况
let emptyArr = [];
console.log(emptyArr.every(() => false)); // 输出:true
console.log(emptyArr.some(() => true)); // 输出:false
六、数组静态方法(工具类)
// 1. Array.isArray():判断是否是数组(比typeof靠谱!)
console.log(Array.isArray([1,2])); // 输出:true
console.log(Array.isArray("123")); // 输出:false
console.log(typeof [1,2]); // 输出:object(typeof判断数组是object,没用)
// 2. Array.from():类数组转真正数组(比如DOM节点、arguments)
let likeArr = {0: "a", 1: "b", length: 2}; // 类数组
let realArr = Array.from(likeArr);
console.log(realArr); // 输出:["a", "b"]
// 3. Array.of():创建数组(替代new Array(),避免坑)
console.log(Array.of(5)); // 输出:[5](把5当元素)
console.log(newArray(5)); // 输出:[empty ×5](把5当长度,坑!)
console.log(Array.of(1,2,3)); // 输出:[1,2,3]
七、新手避坑总结
- 原数组是否改变:push/unshift/pop/shift/reverse/sort/splice会改原数组;concat/slice/map/find等不会改。
- sort默认排序坑
- 遍历选择:简单遍历元素用for...of,需要索引用for/forEach,遍历对象用for...in。
- 空数组判断:every返回true,some返回false,别踩这个坑。
总结
其实JS数组方法不用死记,关键是记住“是否改原数组”、“返回值是什么”、“适用场景”这三点,练几遍就能熟练用。
阅读原文:原文链接
该文章在 2026/2/22 23:28:05 编辑过