目录
[🔍 九、find() 方法:在数组中查找并返回第一个满足条件的元素](#🔍 九、find() 方法:在数组中查找并返回第一个满足条件的元素)
[1. 基本语法](#1. 基本语法)
[2. 基础示例](#2. 基础示例)
[3. 企业开发中的实际应用场景](#3. 企业开发中的实际应用场景)
[4. 注意事项与常见陷阱](#4. 注意事项与常见陷阱)
[🔍 十、findIndex() 方法:告诉目标元素在数组中的位置](#🔍 十、findIndex() 方法:告诉目标元素在数组中的位置)
[1. 基本语法](#1. 基本语法)
[2. 基础示例](#2. 基础示例)
[3. 企业开发中的实际应用场景](#3. 企业开发中的实际应用场景)
[4. 注意事项与常见陷阱](#4. 注意事项与常见陷阱)
[🔍 十一、splice() 方法:实现列表"增删改"最直接的工具](#🔍 十一、splice() 方法:实现列表“增删改”最直接的工具)
[1. 基本语法](#1. 基本语法)
[2. 基础示例](#2. 基础示例)
[3. 企业开发中的实际应用场景](#3. 企业开发中的实际应用场景)
[4. 注意事项与常见陷阱](#4. 注意事项与常见陷阱)
🔍 九、find() 方法:在数组中查找并返回第一个满足条件的元素
find() 方法返回数组中满足提供的测试函数的第一个元素的值 。否则返回 undefined。
简单来说,它的逻辑是:
- 遍历数组。
- 对每个元素执行一次回调函数。
- 如果回调函数返回
true,立即返回当前元素,停止遍历。 - 如果遍历结束都没有返回
true,则最终返回undefined。
1. 基本语法
javascript
arr.find(callback(element[, index[, array]])[, thisArg])
参数解析:
- callback :在数组每一项上执行的函数。
element:当前遍历到的元素(必须)。index:当前遍历到的索引(可选)。array:原数组本身(可选)。
- thisArg :执行回调时用作
this的对象(可选,箭头函数中无效)。
返回值: 数组中第一个满足测试条件的元素;如果没有满足条件的元素,则返回 undefined。
2. 基础示例
(1) 查找基本数据类型
查找数组中第一个大于 10 的元素。
javascript
const numbers = [5, 12, 8, 130, 44];
const found = numbers.find(element => element > 10);
console.log(found); // 输出: 12 (注意:不是 [12, 130, 44],只返回第一个)
(2) 查找对象数组(最常用场景)
这是前端开发中最常见的场景,例如根据 ID 查找用户信息。
javascript
const users = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
{ id: 3, name: 'Charlie', role: 'user' }
];
// 查找 ID 为 2 的用户
const user = users.find(item => item.id === 2);
console.log(user); // 输出: { id: 2, name: 'Bob', role: 'user' }
3. 企业开发中的实际应用场景
在企业级项目中,find() 的应用非常广泛,以下是几个典型场景。
场景 1:列表数据渲染中的"字典翻译"
在后台管理系统中,接口返回的状态通常是数字(如 status: 1),前端需要将其转换为文字显示。通常我们会有一个字典配置数组。
javascript
// 字典配置
const statusOptions = [
{ value: 0, label: '禁用' },
{ value: 1, label: '启用' },
{ value: 2, label: '审核中' }
];
// 接口返回的原始数据
const rowData = { id: 101, name: '商品A', status: 1 };
// 渲染时翻译
const currentStatus = statusOptions.find(opt => opt.value === rowData.status);
console.log(currentStatus?.label); // 输出: '启用'
场景 2:表单编辑时的数据回填
点击"编辑"按钮,需要在一个列表中找到当前行的详细数据,并填充到弹窗表单中。
javascript
const handleEdit = (id) => {
// 从列表中查找对应数据
const record = tableData.value.find(item => item.id === id);
if (record) {
// 进行表单回填
formData.value = { ...record };
showDialog.value = true;
} else {
message.error('未找到对应数据');
}
};
场景 3:权限控制(查找匹配路由)
在前端路由守卫中,判断用户是否有权限访问某个页面,可能需要在权限列表中查找是否存在匹配项。
javascript
const whiteList = ['/login', '/register', '/404'];
// 如果在白名单中找到路径,则允许通过
const canAccess = whiteList.find(path => to.path === path);
// 或者更常用的 some(),但在需要获取匹配项具体内容时用 find()
4. 注意事项与常见陷阱
虽然 find() 好用,但在实际开发中如果不注意细节,可能会引发 Bug。
(1) 区分 find 与 filter
这是新手最容易混淆的点:
filter:返回数组,包含所有满足条件的元素。find:返回元素本身,只返回第一个满足条件的元素。
如果你期望结果是数组(如渲染一个筛选后的列表),请使用 filter。如果你只需要一个结果(如查 ID),请使用 find,性能更好。
(2) 返回 undefined 的处理
find() 找不到元素时会返回 undefined。如果直接解构或访问属性,可能会报错。
错误示范:
javascript
const user = users.find(u => u.id === 999); // 返回 undefined
console.log(user.name); // 报错: Cannot read property 'name' of undefined
正确做法(可选链):
javascript
const user = users.find(u => u.id === 999);
console.log(user?.name); // 输出: undefined (不报错)
(3) 引用类型数据的"副作用"
find() 返回的是数组中元素的引用,而不是副本。这意味着如果你修改了返回的对象,原数组中的数据也会被修改。
javascript
const products = [{ id: 1, price: 100 }];
const product = products.find(p => p.id === 1);
product.price = 200;
console.log(products[0].price); // 输出: 200 (原数组被修改了!)
在企业开发中(特别是 Vue 响应式系统),这可能导致难以排查的数据污染问题。如果不想影响原数组,请使用深拷贝或 { ...product } 创建副本。
(4) 性能考量
find() 在找到第一个匹配项后就会立即停止遍历。相比于 filter() 会遍历整个数组,find() 在处理大量数据且目标靠前时,性能优势明显。
(5) findIndex 的补充
如果你需要的是元素的位置(索引)而不是元素本身,请使用 findIndex()。这在需要删除某个元素(通过索引 splice)时非常有用。
javascript
const index = users.findIndex(u => u.id === 2);
if (index !== -1) {
users.splice(index, 1); // 删除该元素
}
🔍 十、findIndex() 方法:告诉目标元素在数组中的位置
findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引 。若没有找到对应元素,则返回 -1。
它的核心逻辑如下:
- 从左到右遍历数组。
- 对每个元素执行回调函数。
- 一旦回调函数返回
true,立即停止遍历,并返回当前元素的下标。 - 如果遍历结束所有回调都返回
false,则最终返回-1。
1. 基本语法
javascript
arr.findIndex(callback(element[, index[, array]])[, thisArg])
参数解析:
- callback :针对数组中每个元素执行的测试函数。
element:当前元素(必须)。index:当前索引(可选)。array:调用findIndex的数组(可选)。
- thisArg :执行回调时用作
this的对象(可选)。
返回值:
数组中通过测试的第一个元素的索引。如果均未通过测试,则返回 -1。
2. 基础示例
(1) 基础类型查找
查找数组中第一个大于 10 的元素的位置。
javascript
const numbers = [5, 12, 8, 130, 44];
const index = numbers.findIndex(element => element > 10);
console.log(index); // 输出: 1 (因为数字 12 在索引 1 的位置)
(2) 对象数组查找
查找数组中特定属性的对象索引。
javascript
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
// 查找 id 为 3 的用户索引
const index = users.findIndex(user => user.id === 3);
console.log(index); // 输出: 2
(3)未找到的情况
javascript
const index = users.findIndex(user => user.id === 99);
console.log(index); // 输出: -1
3. 企业开发中的实际应用场景
findIndex() 最核心的价值在于精准删除 和定点更新,特别是在 Vue 或 React 的响应式开发中。
场景 1:列表数据的删除操作
这是 findIndex 最经典的应用场景。当后端返回删除成功后,我们需要从前端的列表状态中移除该条数据。
javascript
const handleDelete = (id) => {
// 1. 找到要删除数据的索引
const index = tableData.value.findIndex(item => item.id === id);
// 2. 判断是否找到
if (index !== -1) {
// 3. 使用 splice 进行原地删除
tableData.value.splice(index, 1);
message.success('删除成功');
} else {
message.error('未找到对应数据');
}
};
注:为什么不推荐使用 filter?因为 filter 会返回一个新数组,在某些深层响应式场景下,可能需要重新赋值整个数组,而 splice 配合 findIndex 是一种更轻量的原地操作。

场景 2:列表数据的局部更新
假设我们需要在列表中修改某一条数据的属性(例如点击"展开/折叠"按钮),我们需要先找到它的索引。
javascript
const toggleExpand = (id) => {
const index = listData.value.findIndex(item => item.id === id);
if (index !== -1) {
// 直接通过索引修改,Vue3 中配合 reactive 或 ref 能够触发响应式更新
listData.value[index].isExpanded = !listData.value[index].isExpanded;
}
};
场景 3:去重逻辑判断
在手动实现数组去重或判断数据是否已存在(如购物车添加商品)时,用于检测索引。
javascript
const addToCart = (product) => {
// 查找购物车中是否已有该商品
const existIndex = cartList.value.findIndex(item => item.id === product.id);
if (existIndex !== -1) {
// 已存在,数量 +1
cartList.value[existIndex].count += 1;
} else {
// 不存在,新增
cartList.value.push({ ...product, count: 1 });
}
};
4. 注意事项与常见陷阱
在使用 findIndex 时,有几个关键点需要牢记,以避免产生 Bug。
(1) 返回值的判断:-1 陷阱
这是最容易出错的地方。
find()找不到返回undefined。findIndex()找不到返回-1。
在 if 判断时,千万不能直接写 if (index),因为 index 可能是 0(第一个元素),而 0 是 falsy 值,会导致逻辑错误。
错误示范:
javascript
const index = list.findIndex(item => item.id === targetId);
if (index) { ... } // 如果 index 是 0,条件不成立,逻辑错误!
正确示范:
javascript
if (index !== -1) { ... } // 标准写法
(2) 区分 findIndex 与 indexOf
indexOf:通过全等匹配 (===) 查找值,适用于基本数据类型(字符串、数字)。
javascript
['a', 'b'].indexOf('a'); // 0
findIndex:通过回调函数查找,适用于复杂数据类型(对象)或复杂条件。
javascript
[{id:1}].findIndex(item => item.id === 1); // 0
[{id:1}].indexOf({id:1}); // -1 (对象引用不同)
(3) 性能与短路特性
findIndex 在找到第一个匹配项后会立即停止遍历。如果你的数据量很大(如数千条),且目标靠前,它的性能远优于 forEach 或 filter。
(4) 不要混淆 find 和 findIndex
- 想要数据本身 :用
find()。 - 想要操作位置 (删除、替换、插入):用
findIndex()。
🔍 十一、splice() 方法:实现列表"增删改"最直接的工具
splice() 方法通过删除或替换现有元素 或者在原位置添加新元素来修改数组,并以数组形式返回被修改的内容。
核心特点:
- 会改变原数组。
- 返回值是被删除的元素组成的数组,如果没有删除元素,则返回空数组
[]。
1. 基本语法
javascript
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
参数解析:
- start (必须):指定修改的开始位置。
- 如果超出了数组的长度,则从数组末尾开始添加内容。
- 如果是负数,则从数组末尾开始计算(如
-1指最后一个元素)。
- deleteCount (可选):整数,表示要移除的数组元素的个数。
- 如果为
0,则不删除元素(常用于插入操作)。 - 如果省略或大于
start之后的元素总数,则删除从start开始到数组末尾的所有元素。
- 如果为
- item1, item2, ... (可选):要添加进数组的元素。
- 如果不指定,则
splice()只执行删除操作。
- 如果不指定,则
2. 基础示例
为了方便记忆,我们可以将其用法归纳为三种模式:删、增、改。
(1) 删除元素
从索引 2 开始删除 1 个元素。
javascript
let months = ['Jan', 'Feb', 'Mar', 'Apr'];
let deleted = months.splice(2, 1);
console.log(months); // 输出: ['Jan', 'Feb', 'Apr']
console.log(deleted); // 输出: ['Mar']
(2) 插入元素
在索引 2 的位置,删除 0 个元素,并插入新元素。
javascript
let months = ['Jan', 'Feb', 'Apr'];
months.splice(2, 0, 'Mar');
console.log(months); // 输出: ['Jan', 'Feb', 'Mar', 'Apr']
(3) 替换元素
从索引 0 开始,删除 1 个元素,并插入 'May'。
javascript
let months = ['Jan', 'Feb'];
months.splice(0, 1, 'May');
console.log(months); // 输出: ['May', 'Feb']
3. 企业开发中的实际应用场景
在企业级项目中,splice() 配合 findIndex() 是处理列表数据的标准范式。
场景 1:表格数据的删除操作
这是最经典的业务场景。用户点击"删除"按钮,前端直接操作数据列表,无需重新请求后端接口刷新全量数据。
javascript
const handleDelete = (id) => {
// 1. 找到索引
const index = tableData.value.findIndex(item => item.id === id);
if (index !== -1) {
// 2. 原地删除,界面会自动响应更新
tableData.value.splice(index, 1);
message.success('删除成功');
}
};

场景 2:列表数据的拖拽排序
在拖拽组件(如 vuedraggable)的回调中,经常需要将一个元素从旧位置移除,并插入到新位置。
javascript
const onDragEnd = ({ oldIndex, newIndex }) => {
if (oldIndex === newIndex) return;
// 1. 从旧位置取出该元素(splice 返回的是数组,所以取 [0])
const [movedItem] = list.value.splice(oldIndex, 1);
// 2. 将该元素插入到新位置
list.value.splice(newIndex, 0, movedItem);
// 此时 list.value 已经完成了位置互换
};
场景 3:状态管理中的定点更新
当需要替换数组中某个特定对象的所有属性时。
javascript
const updateUser = (userId, newData) => {
const index = users.value.findIndex(u => u.id === userId);
if (index !== -1) {
// 替换掉索引处的旧对象
users.value.splice(index, 1, newData);
}
};
4. 注意事项与常见陷阱
虽然 splice() 功能强大,但如果使用不当,很容易引发严重的 Bug。
(1) 原地修改的副作用
splice 是"破坏性"方法,它直接修改原数组。
- 陷阱 :如果你使用了 Vue 或 React,且直接修改了被
Object.freeze冻结的数组,或者修改了非响应式数组的副本,视图可能不会更新,甚至报错。 - 建议 :在函数式编程或需要保留原始数据的场景(如"撤销/重做"功能),请使用
toSpliced()(ES2023 新增,不修改原数组)或[...arr.slice(0, index), ...arr.slice(index)]这种展开运算符方式。
(2) 不要在 forEach 或 for...of 循环中使用 splice
这是一个经典的"灾难现场"。在遍历过程中修改数组长度或索引,会导致索引错乱,跳过某些元素。
错误示范:
javascript
// 想要删除所有小于 3 的数
let arr = [1, 2, 3, 4];
arr.forEach((item, index) => {
if (item < 3) {
arr.splice(index, 1); // 删除后,后面的元素前移,索引错位
}
});
console.log(arr); // 输出: [2, 3, 4],数字 2 被跳过了!
正确做法:
- 使用
filter代替(推荐)。 - 如果非要用
splice,请使用倒序遍历for (let i = arr.length - 1; i >= 0; i--)。
(3) 返回值是被删除的元素
很多人误以为 splice 返回修改后的新数组。
javascript
let arr = [1, 2, 3];
let result = arr.splice(0, 1);
console.log(result); // 输出: [1] (是被删掉的元素)
console.log(arr); // 输出: [2, 3] (这才是修改后的数组)
(4) splice vs slice 傻傻分不清楚
这是面试和工作中最常见的混淆:
splice:有p,可以想象成"剪贴板",剪下来贴上去,改变原数组。slice:只有切的动作,像切蛋糕,切下一块给你,原数组不变。