一、filter 方法核心概念
1.1 定义与本质
filter 是 JavaScript 数组的内置迭代方法,属于 ES5 标准(2009 年发布),用于创建一个新数组,其元素是原数组中满足指定条件的所有元素。它的核心本质是'筛选与过滤',通过回调函数对数组元素进行条件判断,保留符合条件的元素并返回新数组。
1.2 核心特性
- 纯函数特性:filter 不会修改原数组,所有筛选操作均基于原数组的副本进行,最终返回全新数组
- 迭代性:遍历原数组的每一个元素,执行相同的条件判断逻辑
- 返回新数组:即使筛选结果为空,也会返回空数组而非 undefined 或 null
- 回调执行时机:仅对数组中已初始化的索引元素执行回调,未初始化的空槽(empty)会被跳过
1.3 应用场景
filter 是前端开发中最常用的数组方法之一,典型应用场景包括:
- 数据筛选(如表格数据过滤、搜索结果匹配)
- 数据清洗(如去除空值、过滤无效数据)
- 数组提纯(如提取特定类型元素、去重处理)
- 条件分组(如按状态拆分数组、按类型分类数据)
二、filter 语法与参数解析
2.1 基本语法
// 基础语法
const newArray = array.filter(callback(element[, index[, array]])[, thisArg]);
2.2 参数详解
2.2.1 回调函数(callback)
必须参数,用于定义筛选条件的函数,返回一个布尔值:
- 返回
true:当前元素会被保留到新数组中 - 返回
false:当前元素会被排除
回调函数接收三个参数:
- element:当前正在处理的数组元素(必须)
- index:当前元素的索引值(可选)
- array:调用 filter 方法的原数组(可选)
2.2.2 thisArg(可选)
可选参数,指定回调函数执行时的 this 指向。若不提供此参数,回调函数中的 this 在非严格模式下指向全局对象(window/global),严格模式下为 undefined。
2.3 返回值
返回一个新的数组,包含所有通过回调函数筛选的元素。新数组的长度由符合条件的元素数量决定,元素顺序与原数组保持一致。
2.4 语法示例
// 完整参数使用示例
const numbers = [10, 20, 30, 40, 50];
const context = { threshold: 30 };
// 使用 thisArg 指定回调中的 this
const filtered = numbers.filter(function(element, index, array) {
console.log(`当前元素:${element},索引:${index},原数组:${array}`);
return element > this.threshold; // this 指向 context 对象
}, context);
console.log(filtered); // 输出:[40, 50]
三、filter 基础用法全解析
3.1 过滤基本数据类型数组
3.1.1 过滤数字数组
// 示例 1:筛选偶数
const nums = [1, 2, 3, 4, 5, 6, 7, 8];
const evenNums = nums.filter(num => num % 2 === 0);
console.log(evenNums); // 输出:[2, 4, 6, 8]
// 示例 2:筛选指定范围的数字
const scores = [85, 92, 78, 65, 98, 59, 88];
const passScores = scores.filter(score => score >= 80);
console.log(passScores); // 输出:[85, 92, 98, 88]
// 示例 3:筛选非 NaN 的数字
const mixedNums = [12, NaN, 34, NaN, 56, undefined, null];
const validNums = mixedNums.filter(num => !isNaN(num) && num !== null && num !== undefined);
console.log(validNums); // 输出:[12, 34, 56]
3.1.2 过滤字符串数组
// 示例 1:筛选长度大于 3 的字符串
const words = ["apple", "cat", "banana", "dog", "grape"];
const longWords = words.filter(word => word.length > 3);
console.log(longWords); // 输出:["apple", "banana", "grape"]
// 示例 2:筛选包含指定字符的字符串
const names = ["张三", "李四", "王五", "张晓明", "赵丽"];
const zhangNames = names.filter(name => name.includes("张"));
console.log(zhangNames); // 输出:["张三", "张晓明"]
// 示例 3:筛选非空字符串
const mixedStrs = ["hello", "", "world", " ", "javascript", null];
const validStrs = mixedStrs.filter(str => typeof str === "string" && str.trim() !== "");
console.log(validStrs); // 输出:["hello", "world", "javascript"]
3.2 过滤对象数组
对象数组是开发中最常用的场景,filter 可基于对象的任意属性进行筛选。
// 数据源:用户列表
const users = [
{ id: 1, name: "张三", age: 25, gender: "male", role: "admin" },
{ id: 2, name: "李四", age: 17, gender: "female", role: "user" },
{ id: 3, name: "王五", age: 32, gender: "male", role: "admin" },
{ id: 4, name: "赵六", age: 28, gender: "male", role: "user" },
{ id: 5, name: "钱七", age: 16, gender: "female", role: "user" }
];
// 示例 1:筛选成年用户(age >= 18)
const adultUsers = users.filter(user => user.age >= 18);
.(adultUsers);
adminUsers = users.( user. === );
.(adminUsers);
adultMaleUsers = users.( user. === && user. >= );
.(adultMaleUsers);
usersWithAddress = [
{ : , : , : { : , : } },
{ : , : , : { : , : } },
{ : , : , : { : , : } }
];
guangdongUsers = usersWithAddress.( user.. === );
.(guangdongUsers);
3.3 过滤特殊值与空值
处理数组中的 null、undefined、空对象等特殊值是数据清洗的常见需求。
// 数据源:包含特殊值的混合数组
const mixedArray = [123, null, "hello", undefined, { name: "张三" }, "", 0, NaN, [], {}, function () {}, true];
// 示例 1:筛选非 null 且非 undefined 的值
const nonNullUndefined = mixedArray.filter(item => item !== null && item !== undefined);
console.log(nonNullUndefined); // 输出:[123, "hello", { name: "张三" }, "", 0, NaN, [], {}, function() {}, true]
// 示例 2:筛选有效数字(排除 NaN、0)
const validNumbers = mixedArray.filter(item => typeof item === "number" && !isNaN(item) && item !== 0);
console.log(validNumbers); // 输出:[123]
// 示例 3:筛选非空对象(排除空对象、数组)
const nonEmptyObjects = mixedArray.filter(item => typeof item === "object" && item !== null && Object.keys(item).length > 0);
console.log(nonEmptyObjects); // 输出:[{ name: "张三" }]
// 示例 4:筛选真值(排除 falsy 值)
truthyValues = mixedArray.();
.(truthyValues);
3.4 过滤类数组对象
filter 是数组的方法,但可通过 Array.prototype.filter.call() 或 Array.from() 应用于类数组对象(如 arguments、NodeList 等)。
// 示例 1:处理 arguments 对象
function filterEvenNumbers() {
// 将 arguments 转换为数组后使用 filter
return Array.from(arguments).filter(num => num % 2 === 0);
}
const evenNums = filterEvenNumbers(1, 2, 3, 4, 5, 6);
console.log(evenNums); // 输出:[2, 4, 6]
// 示例 2:处理 DOM 元素集合(NodeList)
// 实际环境中需在浏览器端运行
// 获取所有按钮元素,筛选出禁用的按钮
// const buttons = document.querySelectorAll("button");
// const disabledButtons = Array.prototype.filter.call(buttons, btn => btn.disabled);
// console.log(disabledButtons); // 输出:所有禁用的按钮元素
// 示例 3:处理字符串(字符串也是类数组)
const str = "javascript";
const vowels = Array.from(str).filter(char => ["a", "e", "i", "o", "u"].includes(char));
console.log(vowels); // 输出:["a", "a", "i"]
四、filter 高级使用技巧
4.1 结合其他数组方法链式调用
filter 常与 map、reduce、sort 等方法结合,实现复杂的数据处理逻辑。
// 数据源:商品列表
const products = [
{ id: 1, name: "手机", category: "电子", price: 3999, stock: 50 },
{ id: 2, name: "衬衫", category: "服装", price: 199, stock: 120 },
{ id: 3, name: "电脑", category: "电子", price: 6999, stock: 30 },
{ id: 4, name: "裤子", category: "服装", price: 299, stock: 80 },
{ id: 5, name: "耳机", category: "电子", price: 499, stock: 100 }
];
// 示例 1:filter + map:筛选电子类商品并提取名称和价格
const electronicProducts = products
.filter(product => product.category === )
.( ({ : product., : product. }));
.(electronicProducts);
clothingStockTotal = products
.( product. === )
.( total + product., );
.(clothingStockTotal);
expensiveProducts = products
.( product. > )
.( a. - b.)
.( product.);
.(expensiveProducts);
4.2 实现数组去重
利用 filter 结合 indexOf 或 includes 可实现数组去重,适用于简单数据类型数组。
// 示例 1:基础去重(利用 indexOf)
const duplicateNums = [1, 2, 2, 3, 3, 3, 4, 5, 5];
const uniqueNums1 = duplicateNums.filter((num, index, array) => {
// 只保留第一次出现的元素(indexOf 返回第一次出现的索引)
return array.indexOf(num) === index;
});
console.log(uniqueNums1); // 输出:[1, 2, 3, 4, 5]
// 示例 2:去重优化(利用 includes)
const duplicateStrs = ["a", "b", "a", "c", "b", "d"];
const uniqueStrs = duplicateStrs.filter((str, index, array) => {
// 检查当前元素是否已在前面出现过
return !array.slice(0, index).includes(str);
});
console.log(uniqueStrs); // 输出:["a", "b", "c", "d"]
// 示例 3:对象数组去重(基于 id 属性)
const duplicateUsers = [
{ id: 1, name: "张三" },
{ id: 2, name: "李四" },
{ id: 1, : },
{ : , : }
];
uniqueUsers = duplicateUsers.( {
existingIds = array.(, index).( item.);
!existingIds.(user.);
});
.(uniqueUsers);
4.3 嵌套数组的过滤
处理多维数组时,可结合递归或 flat 方法与 filter 配合使用。
// 示例 1:二维数组过滤(直接遍历)
const twoDArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
// 筛选所有子数组中的偶数
const evenNumbers = twoDArray.map(subArray => subArray.filter(num => num % 2 === 0));
console.log(evenNumbers); // 输出:[[2], [4, 6], [8]]
// 示例 2:二维数组扁平过滤(先扁平再过滤)
const flatEvenNumbers = twoDArray
.flat() // 先将二维数组转为一维数组
.filter(num => num % 2 === 0);
console.log(flatEvenNumbers); // 输出:[2, 4, 6, 8]
// 示例 3:深层嵌套数组过滤(递归实现)
const deepArray = [1, [2, [3, 4], 5], [6, [7, [8, 9]]]];
// 递归扁平化函数
function flattenArray(arr) {
return arr.reduce((acc, item) => .(item) ? acc.((item)) : acc.(item), []);
}
filteredDeepNums = (deepArray).( num > );
.(filteredDeepNums);
4.4 动态条件筛选
通过动态生成筛选条件,实现灵活的多条件组合筛选。
// 数据源:图书列表
const books = [
{ id: 1, title: "JavaScript 高级程序设计", category: "编程", price: 99, publishYear: 2020 },
{ id: 2, title: "深入理解计算机系统", category: "计算机", price: 128, publishYear: 2018 },
{ id: 3, title: "React 设计模式与最佳实践", category: "编程", price: 79, publishYear: 2021 },
{ id: 4, title: "算法导论", category: "计算机", price: 118, publishYear: 2019 },
{ id: 5, title: "TypeScript 实战", category: "编程", price: 89, publishYear: 2022 }
];
// 动态筛选函数:接收筛选条件对象,返回符合条件的图书
function filterBooks(books, conditions) {
return books.( {
.(conditions).( {
(.(value)) {
value.(book[key]);
}
( value === ) {
(book[key]);
}
book[key] === value;
});
});
}
condition1 = { : , : price < };
result1 = (books, condition1);
.(result1);
condition2 = { : [, ], : year >= };
result2 = (books, condition2);
.(result2);
4.5 在 TypeScript 中使用 filter
TypeScript 中使用 filter 时,可通过类型守卫实现更精确的类型推断。
// 示例 1:基础类型过滤
const mixedValues: (number | string | boolean)[] = [1, "2", 3, "4", true, 5];
// 筛选数字类型,TypeScript 可自动推断结果为 number[]
const numbersOnly = mixedValues.filter(value => typeof value === "number");
// 示例 2:对象类型过滤(类型守卫)
interface User {
id: number;
name: string;
role: "admin" | "user" | "guest";
}
interface Product {
id: number;
name: string;
price: number;
}
// 联合类型数组
const items: (User | Product)[] = [
{ id: 1, name: "张三", role: "admin" },
{ id: 2, name: "手机", price: 3999 },
{ id: , : , : },
{ : , : , : }
];
(): item is {
item;
}
(): item is {
item;
}
users = items.(isUser);
products = items.(isProduct);
4.6 利用 thisArg 绑定上下文
当筛选逻辑需要访问外部对象的属性时,可通过 thisArg 参数绑定上下文。
// 示例:根据配置对象筛选数组
const config = { minScore: 60, maxScore: 100, subject: "math" };
// 学生成绩数据
const studentScores = [
{ name: "张三", math: 85, english: 72 },
{ name: "李四", math: 58, english: 88 },
{ name: "王五", math: 92, english: 65 },
{ name: "赵六", math: 45, english: 90 }
];
// 筛选指定科目成绩在 [minScore, maxScore] 范围内的学生
const qualifiedStudents = studentScores.filter(function(student) {
const score = student[this.subject];
return score >= this.minScore && score <= this.maxScore;
}, config);
// 绑定 this 为 config 对象
console.log(qualifiedStudents); // 输出:张三、王五(math 成绩合格)
五、filter 实战开发案例
5.1 实战案例 1:前端表格数据筛选
实现一个支持多条件筛选的商品表格,包含价格区间、分类筛选、库存状态筛选。
<!--HTML 结构(仅作演示,实际需结合前端框架) -->
<div class="filter-panel">
<select id="category-filter">
<option value="all">所有分类</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
<option value="home">家居用品</option>
</select>
<input type="number" id="min-price" placeholder="最低价格">
<input type="number" id="max-price" placeholder="最高价格">
<select id="stock-filter">
<option value="all">所有库存</option>
< =>有库存
无库存
筛选
5.2 实战案例 2:接口返回数据清洗
处理后端接口返回的复杂数据,筛选有效数据并格式化输出。
// 1. 模拟后端接口返回数据
const apiResponse = {
code: 200,
message: "success",
data: {
users: [
{ id: 1, name: "张三", age: 25, email: "[email protected]", status: 1 },
{ id: 2, name: "", age: 0, email: "[email protected]", status: 0 },
{ id: 3, name: "王五", age: -5, email: "wangwu.example.com", status: 1 },
{ id: 4, name: "赵六", age: 32, email: "[email protected]", status: 1 },
{ id: null, name: "钱七", age: 28, email: "", status: 2 },
{ : , : , : , : , : }
],
:
}
};
emailRegex = ;
() {
(!rawData || rawData. !== || !rawData.?.) {
{ : [], : };
}
cleanedUsers = rawData..
.( user. === && user. > && user. === && user..() !== && user. === && user. > && emailRegex.(user.) && user. === )
.( ({
: user.,
: user..(),
: user.,
: user..()
}));
{ cleanedUsers, : cleanedUsers. };
}
{ cleanedUsers, count } = (apiResponse);
.();
.(, cleanedUsers);
5.3 实战案例 3:数组结构转换与筛选
将扁平数组转换为树形结构,并筛选出包含指定节点的子树。
// 1. 模拟扁平结构的部门数据
const departments = [
{ id: 1, name: "技术部", parentId: 0 },
{ id: 2, name: "产品部", parentId: 0 },
{ id: 3, name: "前端开发", parentId: 1 },
{ id: 4, name: "后端开发", parentId: 1 },
{ id: 5, name: "UI 设计", parentId: 2 },
{ id: 6, name: "产品经理", parentId: 2 },
{ id: 7, name: "React 开发", parentId: 3 },
{ id: 8, name: "Vue 开发", parentId: 3 },
{ id: 9, name: "Java 开发", parentId: 4 },
{ id: 10, name: , : }
];
() {
nodes
.( node. === parentId)
.( ({ ...node, : (nodes, node.) }));
}
() {
tree.( {
(node. === targetNodeId) {
;
}
(node. && node.. > ) {
filteredChildren = (node., targetNodeId);
(filteredChildren. > ) {
node. = filteredChildren;
;
}
}
;
});
}
fullTree = (departments);
.(, fullTree);
filteredTree = (fullTree, );
.(, filteredTree);
六、filter 常见问题与坑点
6.1 原数组不变的特性
filter 不会修改原数组,而是返回新数组。若试图通过 filter 修改原数组元素,会导致意外结果。
// 错误示例:试图通过 filter 修改原数组
const numbers = [1, 2, 3, 4, 5];
const filtered = numbers.filter(num => {
num *= 2; // 修改原数组元素
return num > 5;
});
console.log(filtered); // 输出:[3, 4, 5](实际是 6、8、10 的判断结果)
console.log(numbers); // 输出:[1, 2, 6, 8, 10](原数组被修改,造成副作用)
// 正确做法:筛选与修改分离
const correctFiltered = numbers
.filter(num => num > 2.5) // 先筛选
.map(num => num * 2); // 再修改
console.log(correctFiltered); // 输出:[12, 16, 20]
console.log(numbers); // 输出:[1, 2, 6, 8, 10](原数组保持不变)
6.2 this 指向问题
若未指定 thisArg,回调函数中的 this 指向可能不符合预期,尤其是在箭头函数与普通函数混用的场景。
// 问题示例:this 指向错误
const filterConfig = { threshold: 5 };
const numbers = [1, 3, 5, 7, 9];
// 普通函数作为回调,this 指向全局对象
const filtered1 = numbers.filter(function(num) {
return num > this.threshold; // this.threshold 为 undefined
});
console.log(filtered1); // 输出:[](错误结果)
// 解决方案 1:指定 thisArg 参数
const filtered2 = numbers.filter(function(num) {
return num > this.threshold;
}, filterConfig);
console.log(filtered2); // 输出:[7, 9](正确结果)
// 解决方案 2:使用箭头函数(继承外部 this)
const filtered3 = numbers.filter(num => num > filterConfig.threshold);
console.log(filtered3); // 输出:[7, 9](正确结果)
6.3 空数组与 undefined 的处理
filter 对空数组返回空数组,对包含 undefined 的数组会将其视为 falsy 值过滤。
// 示例 1:空数组处理
const emptyArray = [];
const filteredEmpty = emptyArray.filter(item => item > 0);
console.log(filteredEmpty); // 输出:[](无错误,返回空数组)
// 示例 2:包含 undefined 的数组
const arrayWithUndefined = [1, undefined, 3, undefined, 5];
// 直接筛选会排除 undefined
const filteredUndefined1 = arrayWithUndefined.filter(item => item);
console.log(filteredUndefined1); // 输出:[1, 3, 5]
// 如需保留 undefined,需显式判断
const filteredUndefined2 = arrayWithUndefined.filter(item => item !== undefined);
console.log(filteredUndefined2); // 输出:[1, 3, 5](同上,因为 undefined !== undefined 为 false)
// 如需筛选出 undefined,需反向判断
const onlyUndefined = arrayWithUndefined.filter(item => item === undefined);
console.log(onlyUndefined); // 输出:[undefined, undefined]
6.4 NaN 的过滤问题
由于 NaN !== NaN,直接判断 NaN 会导致筛选失败,需使用 isNaN 函数。
// 问题示例:直接判断 NaN 失败
const arrayWithNaN = [1, 2, NaN, 3, NaN, 4];
// 错误:NaN !== NaN,无法筛选出 NaN
const filteredNaN1 = arrayWithNaN.filter(item => item === NaN);
console.log(filteredNaN1); // 输出:[]
// 正确示例:使用 isNaN 函数
const filteredNaN2 = arrayWithNaN.filter(item => isNaN(item));
console.log(filteredNaN2); // 输出:[NaN, NaN]
// 筛选非 NaN 值
const nonNaNValues = arrayWithNaN.filter(item => !isNaN(item));
console.log(nonNaNValues); // 输出:[1, 2, 3, 4]
6.5 与 find 方法的区别
filter 与 find 容易混淆,核心区别在于返回值:filter 返回所有符合条件的元素数组,find 返回第一个符合条件的元素。
const users = [
{ id: 1, name: "张三", age: 25 },
{ id: 2, name: "李四", age: 30 },
{ id: 3, name: "王五", age: 25 }
];
// 使用 filter:返回所有 25 岁的用户(数组)
const usersAge25 = users.filter(user => user.age === 25);
console.log(usersAge25); // 输出:[{id:1,...}, {id:3,...}]
// 使用 find:返回第一个 25 岁的用户(对象)
const firstUserAge25 = users.find(user => user.age === 25);
console.log(firstUserAge25); // 输出:{id:1, name: "张三", age: 25}
// 性能差异:find 找到第一个匹配项后停止遍历,filter 需遍历整个数组
// 如需获取单个元素,优先使用 find
6.6 稀疏数组的处理
filter 会跳过数组中的空槽(empty),只处理已初始化的元素。
// 创建稀疏数组(索引 1 和 3 为空槽)
const sparseArray = [1, , 3, , 5];
console.log(sparseArray.length); // 输出:5
// filter 跳过空槽
const filteredSparse = sparseArray.filter(item => item > 2);
console.log(filteredSparse); // 输出:[3, 5](空槽被跳过)
// 验证空槽是否被处理
const checkedSparse = sparseArray.filter((item, index) => {
console.log(`索引${index}:${item}`);
return true;
});
// 输出:
// 索引 0:1
// 索引 2:3
// 索引 4:5
// 空槽索引 1 和 3 未被处理
七、filter 性能优化策略
7.1 避免在回调中执行复杂操作
回调函数中的复杂计算会增加迭代成本,尤其是大数据量数组。应将复杂操作移到 filter 外部。
// 优化前:回调中执行复杂计算
const largeArray = Array.from({ length: 100000 }, (_, i) => i);
// 回调中包含复杂的数学计算
console.time("optimize-before");
const resultBefore = largeArray.filter(num => {
// 复杂计算:质数判断
if (num <= 1) return false;
for (let i = 2; i <= Math.sqrt(num); i++) {
if (num % i === 0) return false;
}
return true;
});
console.timeEnd("optimize-before"); // 耗时较长
// 优化后:将复杂计算封装为纯函数,减少回调复杂度(对性能影响较小,但代码更清晰)
// 进一步优化:使用缓存或提前计算(若计算结果可复用)
function isPrime(num) {
if (num <= 1) return false;
for (let i = 2; i <= Math.sqrt(num); i++) {
if (num % i === 0) return false;
}
;
}
.();
resultAfter = largeArray.(isPrime);
.();
7.2 大数据量下的筛选优化
当数组长度超过 10 万时,可考虑分批次筛选或结合其他方法优化。
// 大数据量筛选优化:分批次处理(避免阻塞主线程)
async function filterLargeArray(largeArray, filterFn, batchSize = 10000) {
const result = [];
const totalLength = largeArray.length;
for (let i = 0; i < totalLength; i += batchSize) {
// 分批次处理
const batch = largeArray.slice(i, i + batchSize);
const filteredBatch = batch.filter(filterFn);
result.push(...filteredBatch);
// 每处理一批次,让出主线程(避免 UI 阻塞)
await new Promise(resolve => setTimeout(resolve, 0));
}
return result;
}
// 使用示例
const hugeArray = Array.from({ length: 500000 }, (_, i) => i);
filterLargeArray(hugeArray, num => num % 100 === 0).then(filteredResult => {
console.log(`筛选结果数量:${filteredResult.length}`); // 输出:5000
});
7.3 提前退出筛选(替代方案)
filter 必须遍历整个数组,若只需找到部分符合条件的元素,可使用 for 循环提前退出。
// 场景:从大数据量中筛选前 100 个符合条件的元素
const largeArray = Array.from({ length: 100000 }, (_, i) => i);
// 方案 1:使用 filter(需遍历整个数组)
console.time("filter-full");
const filterResult = largeArray.filter(num => num % 100 === 0).slice(0, 100);
console.timeEnd("filter-full"); // 遍历 10 万次
// 方案 2:使用 for 循环(找到 100 个后提前退出)
console.time("for-early-exit");
const forResult = [];
for (let i = 0; i < largeArray.length; i++) {
if (largeArray[i] % 100 === 0) {
forResult.push(largeArray[i]);
// 找到 100 个后退出循环
if (forResult.length === 100) break;
}
}
console.timeEnd("for-early-exit"); // 仅遍历 10000 次左右,性能提升显著
7.4 避免不必要的类型转换
在回调中频繁进行类型转换会影响性能,应提前统一转换类型。
// 优化前:回调中频繁转换类型
const stringNumbers = Array.from({ length: 100000 }, (_, i) => i.toString());
console.time("convert-in-callback");
const convertedResult1 = stringNumbers.filter(str => {
const num = Number(str); // 每次回调都进行类型转换
return num > 50000;
});
console.timeEnd("convert-in-callback");
// 优化后:提前转换类型,再筛选
console.time("convert-before-filter");
const numbers = stringNumbers.map(str => Number(str)); // 一次性转换
const convertedResult2 = numbers.filter(num => num > 50000);
console.timeEnd("convert-before-filter"); // 性能更优,尤其是大数据量
八、filter 与相似方法的区别
8.1 filter vs map
- filter:用于筛选元素,返回符合条件的元素组成的新数组,数组长度可能减少
- map:用于转换元素,返回与原数组长度相同的新数组,元素值被转换
const numbers = [1, 2, 3, 4, 5];
// filter:筛选偶数(长度减少)
const filtered = numbers.filter(num => num % 2 === 0);
console.log(filtered); // 输出:[2, 4]
// map:将数字翻倍(长度不变)
const mapped = numbers.map(num => num * 2);
console.log(mapped); // 输出:[2, 4, 6, 8, 10]
// 组合使用:先筛选再转换
const combined = numbers.filter(num => num % 2 === 0).map(num => num * 2);
console.log(combined); // 输出:[4, 8]
8.2 filter vs find
- filter:返回所有符合条件的元素数组(可能为空)
- find:返回第一个符合条件的元素(无符合条件元素时返回 undefined)
const users = [
{ id: 1, name: "张三", age: 25 },
{ id: 2, name: "李四", age: 30 },
{ id: 3, name: "王五", age: 25 }
];
// filter:返回所有 25 岁用户
const allAge25 = users.filter(user => user.age === 25);
console.log(allAge25); // 输出:[{id:1,...}, {id:3,...}]
// find:返回第一个 25 岁用户
const firstAge25 = users.find(user => user.age === 25);
console.log(firstAge25); // 输出:{id:1,...}
// 无符合条件时的返回值
const allAge40 = users.filter(user => user.age === 40);
console.log(allAge40); // 输出:[]
const firstAge40 = users.find(user => user.age === 40);
console.(firstAge40);
8.3 filter vs reduce
- filter:专注于筛选元素,功能单一
- reduce:功能更强大,可实现筛选、汇总、转换等多种功能
const numbers = [1, 2, 3, 4, 5, 6];
// 使用 filter 筛选偶数
const filterEven = numbers.filter(num => num % 2 === 0);
console.log(filterEven); // 输出:[2, 4, 6]
// 使用 reduce 实现筛选偶数
const reduceEven = numbers.reduce((acc, num) => {
if (num % 2 === 0) {
acc.push(num);
}
return acc;
}, []);
console.log(reduceEven); // 输出:[2, 4, 6]
// reduce 的额外能力:筛选并计算总和
const evenSum = numbers.reduce((sum, num) => {
return num % 2 === 0 ? sum + num : sum;
}, 0);
console.log(evenSum); // 输出:12(2+4+6)
8.4 filter vs some/every
- filter:返回符合条件的元素数组
- some:判断是否存在符合条件的元素,返回布尔值(找到第一个即停止)
- every:判断所有元素是否都符合条件,返回布尔值(找到第一个不符合即停止)
const scores = [85, 92, 78, 65, 98];
// filter:返回所有及格分数(>=60)
const passedScores = scores.filter(score => score >= 60);
console.log(passedScores); // 输出:[85, 92, 78, 65, 98]
// some:判断是否有满分(100 分)
const hasPerfectScore = scores.some(score => score === 100);
console.log(hasPerfectScore); // 输出:false
// every:判断是否所有分数都及格
const allPassed = scores.every(score => score >= 60);
console.log(allPassed); // 输出:true
// 性能差异:some/every 可提前退出,filter 需遍历全部
总结
JavaScript 的 filter 方法是数组处理的核心工具之一,其纯函数特性、简洁语法和强大的筛选能力使其在前端开发中应用广泛。本文从基础概念出发,详细解析了 filter 的语法参数、基础用法、高级技巧,并通过实战案例展示了其在实际开发中的应用,同时总结了常见问题与性能优化策略。
掌握 filter 方法的关键在于:
- 理解其'筛选 - 返回新数组'的核心逻辑
- 熟练结合其他数组方法实现复杂数据处理
- 注意 this 指向、空值处理等常见坑点
- 根据场景选择合适的优化策略
合理运用 filter 方法可以大幅简化数据筛选代码,提高开发效率和代码可读性。在实际开发中,应结合具体需求,灵活搭配其他数组方法,构建高效、可维护的前端数据处理逻辑。


