深入理解 TypeScript 数组的 find 与 filter 方法:精准查找的艺术

在上一篇关于 some 方法的博客中,我们探讨了如何快速判断数组中是否存在符合条件的元素。今天,我们将深入介绍 TypeScript 中另外两个强大的查找方法:findfilter。这两个方法能够帮助我们精准定位数组中的元素,但在使用场景和返回值上有着本质区别。本文将通过多个典型示例,详细解析它们的使用技巧与最佳实践。

一、方法基础

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

代码解析:

  • 自定义类型守卫 isCirclefindfilter 中都有效

  • 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:链式调用与组合操作

场景:结合 filtermapsort 等进行复杂数据处理。

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 返回 undefinedfilter 返回空数组

  • 使用空值合并运算符 ?? 避免 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 结果数组,无需空值检查
组合性 通常作为终点操作 常作为管道起点

黄金法则

  1. 存在性检查 :使用 some

  2. 查找单个元素 :使用 find

  3. 筛选多个元素 :使用 filter

  4. 获取元素索引 :使用 findIndex

  5. 查找位置并操作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); // 冗长
    }
  }
}

通过本文的深入探讨,我们不仅掌握了 findfilter 的基本用法,更重要的是理解了它们在不同场景下的选择策略。记住这个简单的原则:单个用 find,多个用 filter,配合 TypeScript 的严格类型检查,你的代码将更加健壮、高效!


参考资料:

相关推荐
dsyyyyy11014 小时前
JavaScript变量
开发语言·javascript·ecmascript
kyriewen5 小时前
手写 Promise.all、race、any:不到 30 行代码,解决并发异步的所有姿势
前端·javascript·面试
胡志辉的博客6 小时前
深入浅出理解浏览器事件循环:从一道输出题讲到 Chrome 源码
前端·javascript·chrome·chromium·event loop
代码不加糖6 小时前
js中不会冒泡的事件有哪些?
前端·javascript·vue.js
退休倒计时6 小时前
【每日一题】LeetCode 53. 最大子数组和 TypeScript
数据结构·算法·leetcode·typescript
懂懂tty6 小时前
Vue2与Vue3之间API差异
前端·javascript·vue.js
小二·7 小时前
Next.js 15 全栈开发实战
开发语言·javascript·ecmascript
Rain5098 小时前
2.1 Nest.js 项目初始化与模块化架构
开发语言·前端·javascript·后端·架构·数据分析·node.js
拾年2759 小时前
从零手写 Ajax:用原生 XHR 搭建前后端交互全流程
前端·javascript·ajax
拉勾科研工作室9 小时前
区块链工程毕业论文题目【249个】
开发语言·javascript