一、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 };
const filtered = numbers.filter(function(element, index, array) {
console.log(`当前元素:${element},索引:${index},原数组:${array}`);
return element > this.threshold;
}, context);
console.log(filtered);
三、filter 基础用法全解析
3.1 过滤基本数据类型数组
3.1.1 过滤数字数组
const nums = [1, 2, 3, 4, 5, 6, 7, 8];
const evenNums = nums.filter(num => num % 2 === 0);
console.log(evenNums);
const scores = [85, 92, 78, 65, 98, 59, 88];
const passScores = scores.filter(score => score >= 80);
console.log(passScores);
const mixedNums = [12, NaN, 34, NaN, 56, undefined, null];
const validNums = mixedNums.filter(num => !isNaN(num) && num !== null && num !== undefined);
console.log(validNums);
3.1.2 过滤字符串数组
const words = ["apple", "cat", "banana", "dog", "grape"];
const longWords = words.filter(word => word.length > 3);
console.log(longWords);
const names = ["张三", "李四", "王五", "张晓明", "赵丽"];
const zhangNames = names.filter(name => name.includes("张"));
console.log(zhangNames);
const mixedStrs = ["hello", "", "world", " ", "javascript", null];
const validStrs = mixedStrs.filter(str => typeof str === "string" && str.trim() !== "");
console.log(validStrs);
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" }
];
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];
const nonNullUndefined = mixedArray.filter(item => item !== null && item !== undefined);
console.log(nonNullUndefined);
const validNumbers = mixedArray.filter(item => typeof item === "number" && !isNaN(item) && item !== 0);
console.log(validNumbers);
const nonEmptyObjects = mixedArray.filter(item => typeof item === "object" && item !== null && Object.keys(item).length > 0);
console.log(nonEmptyObjects);
truthyValues = mixedArray.();
.(truthyValues);
3.4 过滤类数组对象
filter 是数组的方法,但可通过 Array.prototype.filter.call() 或 Array.from() 应用于类数组对象(如 arguments、NodeList 等)。
function filterEvenNumbers() {
return Array.from(arguments).filter(num => num % 2 === 0);
}
const evenNums = filterEvenNumbers(1, 2, 3, 4, 5, 6);
console.log(evenNums);
const str = "javascript";
const vowels = Array.from(str).filter(char => ["a", "e", "i", "o", "u"].includes(char));
console.log(vowels);
四、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 }
];
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 可实现数组去重,适用于简单数据类型数组。
const duplicateNums = [1, 2, 2, 3, 3, 3, 4, 5, 5];
const uniqueNums1 = duplicateNums.filter((num, index, array) => {
return array.indexOf(num) === index;
});
console.log(uniqueNums1);
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);
const duplicateUsers = [
{ id: 1, name: "张三" },
{ id: 2, name: "李四" },
{ id: 1, : },
{ : , : }
];
uniqueUsers = duplicateUsers.( {
existingIds = array.(, index).( item.);
!existingIds.(user.);
});
.(uniqueUsers);
4.3 嵌套数组的过滤
处理多维数组时,可结合递归或 flat 方法与 filter 配合使用。
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);
const flatEvenNumbers = twoDArray
.flat()
.filter(num => num % 2 === 0);
console.log(flatEvenNumbers);
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 时,可通过类型守卫实现更精确的类型推断。
const mixedValues: (number | string | boolean)[] = [1, "2", 3, "4", true, 5];
const numbersOnly = mixedValues.filter(value => typeof value === "number");
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 }
];
const qualifiedStudents = studentScores.filter(function(student) {
const score = student[this.subject];
return score >= this.minScore && score <= this.maxScore;
}, config);
console.log(qualifiedStudents);
五、filter 实战开发案例
5.1 实战案例 1:前端表格数据筛选
实现一个支持多条件筛选的商品表格,包含价格区间、分类筛选、库存状态筛选。
<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:接口返回数据清洗
处理后端接口返回的复杂数据,筛选有效数据并格式化输出。
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:数组结构转换与筛选
将扁平数组转换为树形结构,并筛选出包含指定节点的子树。
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 修改原数组元素,会导致意外结果。
const numbers = [1, 2, 3, 4, 5];
const filtered = numbers.filter(num => {
num *= 2;
return num > 5;
});
console.log(filtered);
console.log(numbers);
const correctFiltered = numbers
.filter(num => num > 2.5)
.map(num => num * 2);
console.log(correctFiltered);
console.log(numbers);
6.2 this 指向问题
若未指定 thisArg,回调函数中的 this 指向可能不符合预期,尤其是在箭头函数与普通函数混用的场景。
const filterConfig = { threshold: 5 };
const numbers = [1, 3, 5, 7, 9];
const filtered1 = numbers.filter(function(num) {
return num > this.threshold;
});
console.log(filtered1);
const filtered2 = numbers.filter(function(num) {
return num > this.threshold;
}, filterConfig);
console.log(filtered2);
const filtered3 = numbers.filter(num => num > filterConfig.threshold);
console.log(filtered3);
6.3 空数组与 undefined 的处理
filter 对空数组返回空数组,对包含 undefined 的数组会将其视为 falsy 值过滤。
const emptyArray = [];
const filteredEmpty = emptyArray.filter(item => item > 0);
console.log(filteredEmpty);
const arrayWithUndefined = [1, undefined, 3, undefined, 5];
const filteredUndefined1 = arrayWithUndefined.filter(item => item);
console.log(filteredUndefined1);
const filteredUndefined2 = arrayWithUndefined.filter(item => item !== undefined);
console.log(filteredUndefined2);
const onlyUndefined = arrayWithUndefined.filter(item => item === undefined);
console.log(onlyUndefined);
6.4 NaN 的过滤问题
由于 NaN !== NaN,直接判断 NaN 会导致筛选失败,需使用 isNaN 函数。
const arrayWithNaN = [1, 2, NaN, 3, NaN, 4];
const filteredNaN1 = arrayWithNaN.filter(item => item === NaN);
console.log(filteredNaN1);
const filteredNaN2 = arrayWithNaN.filter(item => isNaN(item));
console.log(filteredNaN2);
const nonNaNValues = arrayWithNaN.filter(item => !isNaN(item));
console.log(nonNaNValues);
6.5 与 find 方法的区别
filter 与 find 容易混淆,核心区别在于返回值:filter 返回所有符合条件的元素数组,find 返回第一个符合条件的元素。
const users = [
{ id: 1, name: "张三", age: 25 },
{ id: 2, name: "李四", age: 30 },
{ id: 3, name: "王五", age: 25 }
];
const usersAge25 = users.filter(user => user.age === 25);
console.log(usersAge25);
const firstUserAge25 = users.find(user => user.age === 25);
console.log(firstUserAge25);
6.6 稀疏数组的处理
filter 会跳过数组中的空槽(empty),只处理已初始化的元素。
const sparseArray = [1, , 3, , 5];
console.log(sparseArray.length);
const filteredSparse = sparseArray.filter(item => item > 2);
console.log(filteredSparse);
const checkedSparse = sparseArray.filter((item, index) => {
console.log(`索引${index}:${item}`);
return true;
});
七、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);
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}`);
});
7.3 提前退出筛选(替代方案)
filter 必须遍历整个数组,若只需找到部分符合条件的元素,可使用 for 循环提前退出。
const largeArray = Array.from({ length: 100000 }, (_, i) => i);
console.time("filter-full");
const filterResult = largeArray.filter(num => num % 100 === 0).slice(0, 100);
console.timeEnd("filter-full");
console.time("for-early-exit");
const forResult = [];
for (let i = 0; i < largeArray.length; i++) {
if (largeArray[i] % 100 === 0) {
forResult.push(largeArray[i]);
if (forResult.length === 100) break;
}
}
console.timeEnd("for-early-exit");
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];
const filtered = numbers.filter(num => num % 2 === 0);
console.log(filtered);
const mapped = numbers.map(num => num * 2);
console.log(mapped);
const combined = numbers.filter(num => num % 2 === 0).map(num => num * 2);
console.log(combined);
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 }
];
const allAge25 = users.filter(user => user.age === 25);
console.log(allAge25);
const firstAge25 = users.find(user => user.age === 25);
console.log(firstAge25);
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];
const filterEven = numbers.filter(num => num % 2 === 0);
console.log(filterEven);
const reduceEven = numbers.reduce((acc, num) => {
if (num % 2 === 0) {
acc.push(num);
}
return acc;
}, []);
console.log(reduceEven);
const evenSum = numbers.reduce((sum, num) => {
return num % 2 === 0 ? sum + num : sum;
}, 0);
console.log(evenSum);
8.4 filter vs some/every
- filter:返回符合条件的元素数组
- some:判断是否存在符合条件的元素,返回布尔值(找到第一个即停止)
- every:判断所有元素是否都符合条件,返回布尔值(找到第一个不符合即停止)
const scores = [85, 92, 78, 65, 98];
const passedScores = scores.filter(score => score >= 60);
console.log(passedScores);
const hasPerfectScore = scores.some(score => score === 100);
console.log(hasPerfectScore);
const allPassed = scores.every(score => score >= 60);
console.log(allPassed);
总结
JavaScript 的 filter 方法是数组处理的核心工具之一,其纯函数特性、简洁语法和强大的筛选能力使其在前端开发中应用广泛。本文从基础概念出发,详细解析了 filter 的语法参数、基础用法、高级技巧,并通过实战案例展示了其在实际开发中的应用,同时总结了常见问题与性能优化策略。
掌握 filter 方法的关键在于:
- 理解其'筛选 - 返回新数组'的核心逻辑
- 熟练结合其他数组方法实现复杂数据处理
- 注意 this 指向、空值处理等常见坑点
- 根据场景选择合适的优化策略
合理运用 filter 方法可以大幅简化数据筛选代码,提高开发效率和代码可读性。在实际开发中,应结合具体需求,灵活搭配其他数组方法,构建高效、可维护的前端数据处理逻辑。