for循环、.map()与.filter()何时性能更优?
基于对象属性值查找对象:循环与数组方法的性能对比
最近针对「如何根据对象的某个属性值从对象数组中查找目标对象」这个问题,我写了一套测试脚本,用来对比不同实现方式的性能和扩展性,先给大家看看测试代码:
const elementsInObject = 1000000; const lengthOfItems = 5; // 生成随机文本的长度 // 生成随机字符串的工具函数 function randomText() { var text = ""; var possible = "abcdefghijklmnopqrstuvwxyz"; for (var i = 0; i < lengthOfItems; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } // 创建测试用的对象数组 console.time("creatingObject"); var arrObj = []; for (let i = 0; i < elementsInObject; i++) { let el = { prop1: randomText(), prop2: randomText(), types: [randomText(), randomText()] }; arrObj.push(el); } console.timeEnd("creatingObject"); // 嵌套for循环实现 console.time("forFor"); var result = []; for (let i = 0; i < arrObj.length; i++) { for(let q=0; q<arrObj[i].types.length; q++){ if (arrObj[i].types[q] == "abcde") { result.push(arrObj[i]); } } } console.timeEnd("forFor"); // for循环+includes实现 console.time("forIncludes"); var result = []; for (let i = 0; i < arrObj.length; i++) { if (arrObj[i].types.includes("abcde")) { result.push(arrObj[i]); } } console.timeEnd("forIncludes"); // filter+includes实现 console.time("filterIncludes"); var result = arrObj.filter(obj => { return obj.types.includes("abcde"); }); console.timeEnd("filterIncludes"); // map+手动收集结果(不推荐的用法) console.time("mapIncludes"); var result = []; arrObj.map(obj => { if (obj.types.includes("abcde")) { result.push(obj); } }); console.timeEnd("mapIncludes"); // filter+indexOf实现 console.time("filterIndexOf"); var result = arrObj.filter(obj => { return obj.types.indexOf("abcde") > -1; }); console.timeEnd("filterIndexOf");
测试结果
在Node.js和Chrome浏览器环境中,嵌套for循环是性能表现最好的实现方式;不过在Firefox里,不同方法的性能差异会有所不同。
核心疑问:filter()或map()何时性能优于for循环?
先明确一点:map()从来都不应该用来做筛选操作——它的设计初衷是转换数组中的每个元素,返回一个新数组,用map来手动收集符合条件的元素属于语义误用,性能自然也不会理想,日常开发里千万别这么写。
至于filter(),它在这些场景下的价值会超过普通for循环:
- 可读性优先的场景:filter()的语义非常清晰,一眼就能看出这段代码是在做「筛选符合条件的元素」操作,代码更简洁,团队协作时更容易理解和维护。如果你的项目更看重代码的可维护性,处理的数据量又不是百万级别的超大数组,filter()是更好的选择。
- 现代引擎优化加持的场景:部分现代JS引擎会对高阶函数做专门的JIT编译优化,当数组元素是简单类型(比如字符串、数字),或者筛选逻辑比较简单时,filter()的性能可能会接近甚至偶尔超过普通for循环。
- 需要链式调用的场景:如果筛选后还要做转换、聚合等操作,filter()可以和map()、reduce()等数组方法无缝链式调用,写出更流畅的函数式代码。比如
arr.filter(...).map(...).reduce(...)这种写法,比嵌套多层for循环要简洁得多,代码的可维护性提升明显。
最后要提醒的是:性能测试一定要结合实际业务场景。如果是处理百万级别的复杂对象数组,普通for循环的性能优势确实很明显;但日常开发中大部分场景都是处理几千条以内的数据,这时候filter()和for循环的性能差异几乎可以忽略不计,优先选择更易读的代码才是明智之举。
内容的提问来源于stack exchange,提问作者Emeeus
相关产品推荐
相关产品推荐

