在上一篇关于 some 方法的博客中,我们探讨了如何快速判断数组中是否存在符合条件的元素。今天,我们将深入介绍 TypeScript 中另外两个强大的查找方法:find 和 filter。这两个方法能够帮助我们精准定位数组中的元素,但在使用场景和返回值上有着本质区别。本文将通过多个典型示例,详细解析它们的使用技巧与最佳实践。
一、方法基础
1.1 find 方法
find() 方法返回数组中第一个满足 测试函数条件的元素,如果没有找到则返回 undefined。
TypeScript
// 方法签名
array.find(callback(element, index, array), thisArg?): T | undefined
核心特性:
-
返回单个元素或
undefined -
具有短路特性,找到即停止遍历
-
不改变原数组
1.2 filter 方法
filter() 方法返回数组中所有满足测试函数条件的元素组成的新数组。
TypeScript
// 方法签名
array.filter(callback(element, index, array), thisArg?): T[]
核心特性:
-
返回新数组(可能为空数组)
-
遍历整个数组,无短路行为
-
不改变原数组
1.3 核心区别对比
表格
复制
| 特性 | find | filter |
|---|---|---|
| 返回值 | 单个元素或 undefined |
新数组(始终为数组) |
| 遍历行为 | 短路,找到即停 | 完整遍历整个数组 |
| 结果数量 | 0 或 1 个 | 0 到 N 个 |
| 使用场景 | 查找唯一项或首个匹配项 | 筛选集合或查找所有匹配项 |
二、典型示例详解
示例 1:基础数值查找
场景:从成绩数组中查找特定分数。
TypeScript
"use strict";
const scores: number[] = [65, 89, 92, 78, 95, 88];
// 使用 find 查找第一个大于90的分数
const firstHighScore: number | undefined = scores.find((score: number): boolean => {
return score > 90;
});
console.log(`第一个高分: ${firstHighScore}`); // 92
console.log(`类型: ${typeof firstHighScore}`); // number
// 查找不存在的元素
const perfectScore: number | undefined = scores.find((score: number): boolean => {
return score === 100;
});
console.log(`满分成绩: ${perfectScore}`); // undefined
console.log(`是否为undefined: ${perfectScore === undefined}`); // true
// 使用 filter 查找所有大于85的分数
const highScores: number[] = scores.filter((score: number): boolean => {
return score > 85;
});
console.log(`所有高分: [${highScores.join(", ")}]`); // [89, 92, 95, 88]
console.log(`返回类型: ${Array.isArray(highScores)}`); // true
// filter 返回空数组的情况
const lowScores: number[] = scores.filter((score: number): boolean => {
return score < 60;
});
console.log(`不及格分数: [${lowScores.join(", ")}]`); // []
console.log(`空数组长度: ${lowScores.length}`); // 0
代码解析:
-
find返回number | undefined,必须处理未找到的情况 -
filter始终返回数组,即使为空,这在严格模式下更安全 -
使用
join方法优雅地打印数组内容
示例 2:对象数组精准查找
场景:用户管理系统中查找特定用户。
TypeScript
"use strict";
interface User {
id: number;
username: string;
email: string;
isActive: boolean;
roles: string[];
lastLogin: Date | null;
}
const users: User[] = [
{ id: 1, username: "alice", email: "alice@example.com", isActive: true, roles: ["user"], lastLogin: new Date("2024-01-15") },
{ id: 2, username: "bob", email: "bob@example.com", isActive: false, roles: ["user", "admin"], lastLogin: null },
{ id: 3, username: "charlie", email: "charlie@example.com", isActive: true, roles: ["user", "editor"], lastLogin: new Date("2024-01-20") },
{ id: 4, username: "david", email: "david@example.com", isActive: true, roles: ["user"], lastLogin: new Date("2024-01-10") }
];
// 使用 find 查找特定ID的用户
const targetUser: User | undefined = users.find((user: User): boolean => {
return user.id === 2;
});
if (targetUser) {
console.log(`找到用户: ${targetUser.username}`);
console.log(`邮箱: ${targetUser.email}`);
console.log(`活跃状态: ${targetUser.isActive}`);
} else {
console.log("用户未找到");
}
// 安全访问可能为null的属性
const lastLoginDate: string = targetUser?.lastLogin?.toLocaleDateString() ?? "从未登录";
console.log(`最后登录: ${lastLoginDate}`); // 从未登录
// 使用 filter 查找所有活跃用户
const activeUsers: User[] = users.filter((user: User): boolean => {
return user.isActive === true;
});
console.log(`\n活跃用户列表 (${activeUsers.length}人):`);
activeUsers.forEach((user: User) => {
console.log(`- ${user.username} (${user.email})`);
});
// 复杂条件:查找拥有admin角色的用户
const adminUsers: User[] = users.filter((user: User): boolean => {
return user.roles.includes("admin");
});
console.log(`\n管理员用户 (${adminUsers.length}人):`);
adminUsers.forEach((user: User) => {
console.log(`- ${user.username}`);
});
输出结果:
找到用户: bob
邮箱: bob@example.com
活跃状态: false
最后登录: 从未登录
活跃用户列表 (3人):
- alice (alice@example.com)
- charlie (charlie@example.com)
- david (david@example.com)
管理员用户 (1人):
- bob
代码解析:
-
使用可选链(
?.)安全访问可能不存在的属性 -
find后必须进行undefined检查,这是严格模式的要求 -
filter的结果可以直接使用,无需空值检查
示例 3:复杂条件与多属性筛选
场景:电商商品多维度筛选。
TypeScript
"use strict";
interface Product {
id: string;
name: string;
category: "electronics" | "clothing" | "books" | "home";
price: number;
stock: number;
rating: number;
isFeatured: boolean;
tags: string[];
}
const products: Product[] = [
{ id: "P001", name: "MacBook Pro", category: "electronics", price: 12999, stock: 5, rating: 4.8, isFeatured: true, tags: ["laptop", "apple"] },
{ id: "P002", name: "iPhone 15", category: "electronics", price: 6999, stock: 0, rating: 4.7, isFeatured: true, tags: ["phone", "apple"] },
{ id: "P003", name: "编程书籍套装", category: "books", price: 299, stock: 50, rating: 4.5, isFeatured: false, tags: ["programming", "education"] },
{ id: "P004", name: "纯棉T恤", category: "clothing", price: 99, stock: 100, rating: 4.2, isFeatured: false, tags: ["cotton", "casual"] },
{ id: "P005", name: "智能台灯", category: "home", price: 399, stock: 3, rating: 4.0, isFeatured: true, tags: ["lighting", "smart"] }
];
// find: 查找第一个有库存的推荐商品
const availableFeaturedProduct: Product | undefined = products.find((product: Product): boolean => {
return product.isFeatured && product.stock > 0;
});
if (availableFeaturedProduct) {
console.log(`推荐商品: ${availableFeaturedProduct.name}`);
console.log(`价格: ¥${availableFeaturedProduct.price}`);
} else {
console.log("暂无推荐商品");
}
// filter: 多条件筛选 - 电子产品且评分高于4.5且有库存
const premiumElectronics: Product[] = products.filter((product: Product): boolean => {
return product.category === "electronics" &&
product.rating >= 4.5 &&
product.stock > 0;
});
console.log(`\n高端电子产品 (${premiumElectronics.length}件):`);
premiumElectronics.forEach((product: Product) => {
console.log(`- ${product.name} (库存: ${product.stock}, 评分: ${product.rating})`);
});
// filter: 标签筛选 - 包含"apple"或"smart"的商品
const techProducts: Product[] = products.filter((product: Product): boolean => {
return product.tags.some((tag: string): boolean => tag === "apple" || tag === "smart");
});
console.log(`\n科技产品 (${techProducts.length}件):`);
techProducts.forEach((product: Product) => {
console.log(`- ${product.name} (标签: ${product.tags.join(", ")})`);
});
// 价格区间筛选
const budgetRange: [number, number] = [200, 500];
const budgetProducts: Product[] = products.filter((product: Product): boolean => {
return product.price >= budgetRange[0] && product.price <= budgetRange[1];
});
console.log(`\n预算范围内商品 (${budgetRange[0]}-${budgetRange[1]}元):`);
budgetProducts.forEach((product: Product) => {
console.log(`- ${product.name}: ¥${product.price} (库存: ${product.stock})`);
});
输出结果:
推荐商品: MacBook Pro
价格: ¥12999
高端电子产品 (1件):
- MacBook Pro (库存: 5, 评分: 4.8)
科技产品 (2件):
- MacBook Pro (标签: laptop, apple)
- iPhone 15 (标签: phone, apple)
预算范围内商品 (2件):
- 编程书籍套装: ¥299 (库存: 50)
- 智能台灯: ¥399 (库存: 3)
代码解析:
-
组合多个条件进行精准筛选
-
在
filter内部嵌套使用some方法检查标签数组 -
使用元组类型
[number, number]定义价格区间 -
返回结果保持了原数组中对象的引用
示例 4:类型守卫与类型缩小
场景:在联合类型数组中安全地查找特定类型元素。
TypeScript
"use strict";
type Shape = Circle | Rectangle | Triangle;
interface Circle {
kind: "circle";
radius: number;
getArea(): number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
getArea(): number;
}
interface Triangle {
kind: "triangle";
base: number;
height: number;
getArea(): number;
}
const shapes: Shape[] = [
{ kind: "circle", radius: 5, getArea: function() { return Math.PI * this.radius ** 2; } },
{ kind: "rectangle", width: 4, height: 6, getArea: function() { return this.width * this.height; } },
{ kind: "circle", radius: 3, getArea: function() { return Math.PI * this.radius ** 2; } },
{ kind: "triangle", base: 5, height: 8, getArea: function() { return 0.5 * this.base * this.height; } }
];
// 自定义类型守卫
function isCircle(shape: Shape): shape is Circle {
return shape.kind === "circle";
}
function isRectangle(shape: Shape): shape is Rectangle {
return shape.kind === "rectangle";
}
// 使用带类型守卫的 find
const firstCircle: Circle | undefined = shapes.find(isCircle);
if (firstCircle) {
console.log(`找到第一个圆形,半径: ${firstCircle.radius}`);
console.log(`面积: ${firstCircle.getArea().toFixed(2)}`);
} else {
console.log("未找到圆形");
}
// 使用 filter 和类型守卫获取所有圆形
const allCircles: Circle[] = shapes.filter(isCircle);
console.log(`\n所有圆形 (${allCircles.length}个):`);
allCircles.forEach((circle: Circle, index: number) => {
console.log(`圆形 ${index + 1}: 半径=${circle.radius}, 面积=${circle.getArea().toFixed(2)}`);
});
// 复杂条件:查找面积大于50的形状
const largeShapes: Shape[] = shapes.filter((shape: Shape): boolean => {
return shape.getArea() > 50;
});
console.log(`\n大面积图形 (${largeShapes.length}个):`);
largeShapes.forEach((shape: Shape) => {
console.log(`${shape.kind}: 面积=${shape.getArea().toFixed(2)}`);
});
// 类型缩小后的属性访问
const rectangles: Rectangle[] = shapes.filter(isRectangle);
if (rectangles.length > 0) {
const firstRect: Rectangle = rectangles[0];
// 现在可以安全访问 Rectangle 特有属性
console.log(`\n第一个矩形的宽高比: ${(firstRect.width / firstRect.height).toFixed(2)}`);
}
输出结果:
找到第一个圆形,半径: 5
面积: 78.54
所有圆形 (2个):
圆形 1: 半径=5, 面积=78.54
圆形 2: 半径=3, 面积=28.27
大面积图形 (1个):
circle: 面积=78.54
第一个矩形的宽高比: 0.67
代码解析:
-
自定义类型守卫
isCircle在find和filter中都有效 -
filter配合类型守卫可以返回精确的类型数组(Circle[]) -
避免了类型断言,享受完整的 TypeScript 类型检查
-
在回调函数中可以安全调用
getArea方法
示例 5:短路特性与性能对比
场景:演示 find 的短路特性,对比 filter 的完整遍历。
TypeScript
"use strict";
const largeData: number[] = Array.from({ length: 100000 }, (_, i) => i);
let findIterations: number = 0;
let filterIterations: number = 0;
// find 的短路特性
console.time("find 性能");
const foundItem: number | undefined = largeData.find((num: number): boolean => {
findIterations++;
return num === 50000;
});
console.timeEnd("find 性能");
console.log(`find 找到的值: ${foundItem}`);
console.log(`find 迭代次数: ${findIterations}`); // 50001
// filter 的完整遍历
console.time("filter 性能");
const filteredItems: number[] = largeData.filter((num: number): boolean => {
filterIterations++;
return num === 50000;
});
console.timeEnd("filter 性能");
console.log(`filter 找到的数量: ${filteredItems.length}`);
console.log(`filter 迭代次数: ${filterIterations}`); // 100000
// 查找不存在的元素
findIterations = 0;
console.time("find 未找到");
const notFound: number | undefined = largeData.find((num: number): boolean => {
findIterations++;
return num === 200000; // 不存在
});
console.timeEnd("find 未找到");
console.log(`find 未找到结果: ${notFound}`);
console.log(`find 完整遍历次数: ${findIterations}`); // 100000
// 实际应用:查找第一个可用的服务器
interface Server {
id: string;
load: number;
isOnline: boolean;
}
const servers: Server[] = [
{ id: "srv-01", load: 85, isOnline: true },
{ id: "srv-02", load: 92, isOnline: true },
{ id: "srv-03", load: 45, isOnline: true },
{ id: "srv-04", load: 78, isOnline: false },
{ id: "srv-05", load: 38, isOnline: true }
];
// 使用 find 快速定位低负载服务器
const optimalServer: Server | undefined = servers.find((server: Server): boolean => {
console.log(`检查服务器 ${server.id}, 负载: ${server.load}%`);
return server.isOnline && server.load < 50;
});
if (optimalServer) {
console.log(`\n选择服务器: ${optimalServer.id} (负载: ${optimalServer.load}%)`);
} else {
console.log("\n没有找到合适的服务器");
}
输出结果:
find 性能: 3.5ms
find 找到的值: 50000
find 迭代次数: 50001
filter 性能: 7.2ms
filter 找到的数量: 1
filter 迭代次数: 100000
find 未找到: 2.8ms
find 未找到结果: undefined
find 完整遍历次数: 100000
检查服务器 srv-01, 负载: 85%
检查服务器 srv-02, 负载: 92%
检查服务器 srv-03, 负载: 45%
选择服务器: srv-03 (负载: 45%)
代码解析:
-
find在找到匹配项后立即停止,性能优势明显 -
filter必须遍历整个数组,确保找到所有匹配项 -
在负载均衡等场景中,
find更适合快速选择
示例 6:链式调用与组合操作
场景:结合 filter、map、sort 等进行复杂数据处理。
TypeScript
"use strict";
interface Order {
orderId: string;
customerId: number;
amount: number;
status: "pending" | "processing" | "completed" | "cancelled";
items: string[];
createdAt: Date;
}
const orders: Order[] = [
{ orderId: "ORD-001", customerId: 1001, amount: 2500, status: "completed", items: ["A", "B"], createdAt: new Date("2024-01-10") },
{ orderId: "ORD-002", customerId: 1002, amount: 1800, status: "pending", items: ["C"], createdAt: new Date("2024-01-12") },
{ orderId: "ORD-003", customerId: 1001, amount: 3200, status: "processing", items: ["A", "D", "E"], createdAt: new Date("2024-01-15") },
{ orderId: "ORD-004", customerId: 1003, amount: 1500, status: "completed", items: ["B", "F"], createdAt: new Date("2024-01-16") },
{ orderId: "ORD-005", customerId: 1002, amount: 4500, status: "completed", items: ["G", "H", "I", "J"], createdAt: new Date("2024-01-18") }
];
// 1. 筛选 + 映射: 找出所有已完成订单的客户ID
const completedCustomerIds: number[] = orders
.filter((order: Order): boolean => order.status === "completed")
.map((order: Order): number => order.customerId);
console.log("完成订单的客户ID:", [...new Set(completedCustomerIds)]); // 去重
// 2. 筛选 + 排序: 找出金额大于2000的订单并按金额降序
const highValueOrders: Order[] = orders
.filter((order: Order): boolean => order.amount > 2000)
.sort((a: Order, b: Order): number => b.amount - a.amount);
console.log("\n高价值订单(降序):");
highValueOrders.forEach((order: Order) => {
console.log(`- ${order.orderId}: ¥${order.amount} (${order.status})`);
});
// 3. 筛选 + 统计: 计算每个客户的订单数量
const customerOrderCounts: Map<number, number> = orders
.filter((order: Order): boolean => order.status !== "cancelled")
.reduce((acc: Map<number, number>, order: Order) => {
const count: number = acc.get(order.customerId) ?? 0;
acc.set(order.customerId, count + 1);
return acc;
}, new Map<number, number>());
console.log("\n客户订单统计:");
customerOrderCounts.forEach((count: number, customerId: number) => {
console.log(`- 客户 ${customerId}: ${count} 个订单`);
});
// 4. 使用 find 查找特定客户的最新订单
const targetCustomerId: number = 1001;
const customerOrders: Order[] = orders.filter((order: Order): boolean => order.customerId === targetCustomerId);
const latestOrder: Order | undefined = customerOrders
.sort((a: Order, b: Order): number => b.createdAt.getTime() - a.createdAt.getTime())[0];
if (latestOrder) {
console.log(`\n客户 ${targetCustomerId} 的最新订单:`);
console.log(`- 订单号: ${latestOrder.orderId}`);
console.log(`- 金额: ¥${latestOrder.amount}`);
console.log(`- 状态: ${latestOrder.status}`);
console.log(`- 商品数量: ${latestOrder.items.length}件`);
}
// 5. 复杂筛选: 找出包含特定商品且状态为processing的订单
const targetItem: string = "A";
const specialOrders: Order[] = orders.filter((order: Order): boolean => {
return order.status === "processing" && order.items.includes(targetItem);
});
console.log(`\n包含商品"${targetItem}"且处理中的订单:`, specialOrders.map(o => o.orderId));
输出结果:
完成订单的客户ID: [ 1001, 1003, 1002 ]
高价值订单(降序):
- ORD-005: ¥4500 (completed)
- ORD-003: ¥3200 (processing)
- ORD-001: ¥2500 (completed)
客户订单统计:
- 客户 1001: 2 个订单
- 客户 1002: 2 个订单
- 客户 1003: 1 个订单
客户 1001 的最新订单:
- 订单号: ORD-003
- 金额: ¥3200
- 状态: processing
- 商品数量: 3件
包含商品"A"且处理中的订单: [ 'ORD-003' ]
代码解析:
-
展示了函数式编程的链式调用风格
-
每一步都保持类型安全,TypeScript 能正确推断类型
-
filter作为数据管道的起点,为后续操作提供精确数据 -
结合
reduce进行聚合统计,结合map进行数据转换
示例 7:与 findIndex 的配合使用
场景:先查找元素,再获取其索引进行操作。
"use strict";
interface Task {
id: string;
title: string;
priority: "low" | "medium" | "high";
completed: boolean;
}
let tasks: Task[] = [
{ id: "T001", title: "编写文档", priority: "high", completed: false },
{ id: "T002", title: "代码审查", priority: "medium", completed: true },
{ id: "T003", title: "修复Bug", priority: "high", completed: false },
{ id: "T004", title: "更新依赖", priority: "low", completed: false }
];
// 查找特定任务
const criticalTask: Task | undefined = tasks.find((task: Task): boolean => task.id === "T003");
if (criticalTask) {
console.log(`找到关键任务: ${criticalTask.title}`);
// 获取索引以便修改原数组
const taskIndex: number = tasks.findIndex((task: Task): boolean => task.id === "T003");
if (taskIndex !== -1) {
// 模拟更新任务状态
tasks[taskIndex] = { ...tasks[taskIndex], completed: true };
console.log(`任务 ${tasks[taskIndex].title} 已标记为完成`);
}
}
// 查找所有未完成的紧急任务
const urgentPendingTasks: Task[] = tasks.filter((task: Task): boolean => {
return task.priority === "high" && !task.completed;
});
console.log(`\n未完成的紧急任务 (${urgentPendingTasks.length}个):`);
urgentPendingTasks.forEach((task: Task) => {
console.log(`- ${task.title}`);
});
// 批量操作:删除所有已完成的任务
const completedTaskIds: string[] = tasks
.filter((task: Task): boolean => task.completed)
.map((task: Task): string => task.id);
console.log(`\n要删除的已完成任务ID:`, completedTaskIds);
// 保留未完成的任务
tasks = tasks.filter((task: Task): boolean => !task.completed);
console.log("删除后任务列表:");
tasks.forEach((task: Task) => {
console.log(`- ${task.title} (完成: ${task.completed})`);
});
输出结果:
找到关键任务: 修复Bug
任务 修复Bug 已标记为完成
未完成的紧急任务 (1个):
- 编写文档
要删除的已完成任务ID: [ 'T002', 'T003' ]
删除后任务列表:
- 编写文档 (完成: false)
- 更新依赖 (完成: false)
代码解析:
-
find获取对象,findIndex获取位置,组合使用实现更新 -
filter配合map提取需要操作的ID列表 -
使用展开运算符
{ ... }创建新对象,保持不可变性 -
最后用
filter实现"删除"操作(实际上返回新数组)
示例 8:边界情况与错误处理
场景:严格模式下的空值处理、类型兼容性问题。
TypeScript
"use strict";
// 1. 空数组处理
const emptyArray: string[] = [];
const notFound: string | undefined = emptyArray.find((item: string): boolean => item.length > 0);
const filteredEmpty: string[] = emptyArray.filter((item: string): boolean => item.length > 0);
console.log("空数组find结果:", notFound); // undefined
console.log("空数组filter结果:", filteredEmpty); // []
console.log("filter结果是否为数组:", Array.isArray(filteredEmpty)); // true
// 2. 类型收窄与类型断言
interface ApiResponse {
data: User[] | null;
}
const response: ApiResponse = {
data: null
};
// ❌ 错误:可能调用null的方法
// const users = response.data.filter(u => u.isActive);
// ✅ 正确:空值检查 + 类型断言
const activeUsers: User[] = (response.data ?? []).filter((user: User): boolean => user.isActive);
// 3. 稀疏数组处理
const sparseArray: (number | undefined)[] = [1, , 3, , 5]; // 包含空位
const foundInSparse: number | undefined = sparseArray.find((num: number | undefined): boolean => {
return num === undefined;
});
console.log("稀疏数组find结果:", foundInSparse); // undefined(第一个空位)
const filteredSparse: (number | undefined)[] = sparseArray.filter((num: number | undefined): boolean => {
return num !== undefined;
});
console.log("稀疏数组filter结果:", filteredSparse); // [1, 3, 5]
// 4. 类型不匹配处理
const mixedData: (number | string)[] = [1, "hello", 3, "world", 5];
// 过滤出数字并计算总和
const numbersOnly: number[] = mixedData.filter((item: number | string): item is number => {
return typeof item === "number";
});
const sum: number = numbersOnly.reduce((acc: number, num: number): number => acc + num, 0);
console.log("数字总和:", sum); // 9
// 查找第一个字符串并转换为大写
const firstString: string | undefined = mixedData.find((item: number | string): item is string => {
return typeof item === "string";
});
const uppercase: string | undefined = firstString?.toUpperCase();
console.log("第一个字符串大写:", uppercase); // HELLO
输出结果:
空数组find结果: undefined
空数组filter结果: []
filter结果是否为数组: true
稀疏数组find结果: undefined
稀疏数组filter结果: [ 1, 3, 5 ]
数字总和: 9
第一个字符串大写: HELLO
代码解析:
-
空数组调用
find返回undefined,filter返回空数组 -
使用空值合并运算符
??避免null调用 -
稀疏数组中,
find会跳过空位,但filter会移除空位 -
自定义类型守卫
item is number实现类型过滤
三、最佳实践与性能考量
3.1 何时使用 find vs filter
TypeScript
"use strict";
const inventory: { sku: string; quantity: number }[] = [
{ sku: "A001", quantity: 5 },
{ sku: "A002", quantity: 0 },
{ sku: "A003", quantity: 10 }
];
// ✅ 推荐:查找唯一标识符,使用 find
const product: { sku: string; quantity: number } | undefined = inventory.find(item => item.sku === "A002");
if (product) {
console.log(`库存状态: ${product.quantity > 0 ? "有货" : "缺货"}`);
}
// ❌ 避免:用 filter 查找单个唯一项(性能差)
const productArray: { sku: string; quantity: number }[] = inventory.filter(item => item.sku === "A002");
if (productArray.length > 0) {
console.log(`低效方式结果: ${productArray[0].quantity}`);
}
// ✅ 推荐:查找所有匹配项,使用 filter
const inStockItems: { sku: string; quantity: number }[] = inventory.filter(item => item.quantity > 0);
console.log(`有货商品 (${inStockItems.length}件):`, inStockItems.map(i => i.sku));
// ❌ 避免:用 find 在循环中查找多个项(错误用法)
const inStockSkus: string[] = [];
for (const sku of ["A001", "A003"]) {
const item = inventory.find(i => i.sku === sku);
if (item && item.quantity > 0) inStockSkus.push(item.sku);
}
// 正确做法是直接使用 filter
3.2 TypeScript 严格模式注意事项
TypeScript
"use strict";
const data: (string | null)[] = ["a", null, "b", null, "c"];
// ❌ 错误:未处理 null 情况
// const lengths = data.find(item => item !== null).length;
// ✅ 正确:类型收窄
const foundItem: string | null | undefined = data.find((item: string | null): item is string => {
return item !== null;
});
if (foundItem !== null && foundItem !== undefined) {
console.log(`找到非null项,长度: ${foundItem.length}`);
}
// ❌ 错误:filter 后可能仍有 null
// const filtered: string[] = data.filter(item => item !== null);
// ✅ 正确:使用类型守卫
const filtered: string[] = data.filter((item: string | null): item is string => {
return item !== null;
});
console.log("过滤后数组:", filtered); // ["a", "b", "c"]
3.3 性能基准测试
TypeScript
"use strict";
const hugeArray: number[] = Array.from({ length: 1_000_000 }, (_, i) => i);
const target: number = 999_999;
// 测试1: find 查找末尾元素
console.time("find 末尾");
const findResult: number | undefined = hugeArray.find(n => n === target);
console.timeEnd("find 末尾");
console.log("find 结果:", findResult);
// 测试2: filter 查找单元素
console.time("filter 单元素");
const filterResult: number[] = hugeArray.filter(n => n === target);
console.timeEnd("filter 单元素");
console.log("filter 结果:", filterResult[0]);
// 测试3: for 循环提前 break
console.time("for 循环");
let forResult: number | undefined;
for (let i: number = 0; i < hugeArray.length; i++) {
if (hugeArray[i] === target) {
forResult = hugeArray[i];
break;
}
}
console.timeEnd("for 循环");
console.log("for 结果:", forResult);
// 结论:find 性能 ≈ for 循环,filter 性能差
3.4 内存使用考量
TypeScript
"use strict";
const originalArray: number[] = Array.from({ length: 100000 }, (_, i) => i);
// filter 会创建新数组,增加内存占用
const filtered: number[] = originalArray.filter(n => n % 2 === 0);
console.log(`原数组长度: ${originalArray.length}`);
console.log(`新数组长度: ${filtered.length}`);
console.log(`内存占用增加: ~${(filtered.length * 8 / 1024 / 1024).toFixed(2)} MB`);
// find 不创建新数组,内存友好
const found: number | undefined = originalArray.find(n => n === 50000);
console.log(`find 结果内存占用: 无额外数组开销`);
// 大数据量场景建议
// 如果需要频繁查找,考虑使用 Map/Set
const lookupMap: Map<number, boolean> = new Map(
originalArray.map(n => [n, true])
);
const fastLookup: boolean = lookupMap.has(50000); // O(1) 复杂度
四、总结
核心要点回顾
表格
复制
| 维度 | find | filter |
|---|---|---|
| 返回值 | `T | undefined` |
| 性能 | O(n) 平均,可提前终止 | O(n) 必须遍历全部 |
| 使用场景 | 查找唯一项、首个匹配项 | 查找所有匹配项、数据筛选 |
| 类型安全 | 必须检查undefined | 结果数组,无需空值检查 |
| 组合性 | 通常作为终点操作 | 常作为管道起点 |
黄金法则
-
存在性检查 :使用
some -
查找单个元素 :使用
find -
筛选多个元素 :使用
filter -
获取元素索引 :使用
findIndex -
查找位置并操作 :
findIndex+ 索引访问
实际开发建议
TypeScript
"use strict";
// ✅ 推荐模式
function processUser(userId: number): void {
// 1. 查找用户
const user = users.find(u => u.id === userId);
if (!user) {
throw new Error(`用户 ${userId} 不存在`);
}
// 2. 筛选相关数据
const userOrders = orders.filter(o => o.customerId === userId);
// 3. 检查条件
const hasHighValueOrder = userOrders.some(o => o.amount > 1000);
// 4. 执行业务逻辑
// ...
}
// ❌ 反模式
function badPractice(userId: number): void {
// 不要用 filter 找单个元素
const user = users.filter(u => u.id === userId)[0]; // 性能差
// 不要用 find 找多个元素
const userOrders: Order[] = [];
for (const order of orders) {
if (order.customerId === userId) {
userOrders.push(order); // 冗长
}
}
}
通过本文的深入探讨,我们不仅掌握了 find 和 filter 的基本用法,更重要的是理解了它们在不同场景下的选择策略。记住这个简单的原则:单个用 find,多个用 filter,配合 TypeScript 的严格类型检查,你的代码将更加健壮、高效!
参考资料: