js里面天天都在使用的数组你真的搞懂了吗???

张开发
2026/6/10 1:04:54 15 分钟阅读

分享文章

js里面天天都在使用的数组你真的搞懂了吗???
作为前端开发者数组是我们日常开发和面试中最常接触的数据结构。但你真的懂JS数组吗比如为什么new Array(7).fill([])会踩坑push和slice的本质区别是什么forEach为啥不能中途break这篇文章把JS数组的核心知识点拆成7个实战模块从基础创建到遍历、从纯函数到二维数组全是干货看完就能用帮你彻底搞懂JS数组的底层逻辑和实战技巧。一、先搞懂JS数组和其他语言的不一样其他语言的数组要求元素类型一致、长度固定内存布局严格连续。JS数组天生灵活不用限制元素类型也不用固定长度开箱即用但底层依然是「地址偏移量」的内存逻辑。这既是JS的优势也是新手容易踩坑的点——灵活的背后藏着不少容易忽略的细节。二、数组创建3种方式避坑点1. 最常用字面量创建constarr[1,a,true,{}];// 元素类型随意无需指定长度console.log(arr);// [1, a, true, {}]✅ 优点直观、灵活日常开发首选。2. 指定长度new Array()constarrnewArray(7);console.log(arr);// [empty × 7]console.log(arr[0]);// undefined⚠️ 踩坑提醒empty表示该位置未赋值不属于任何数据类型直接访问arr[0]返回undefined但和主动赋值undefined不是一回事。3. 固定长度固定值fill()// 正确用法创建长度为7、每个元素都是1的数组constarrnewArray(7).fill(1);console.log(arr);// [1, 1, 1, 1, 1, 1, 1]// 踩坑用法填充引用类型constbadArrnewArray(3).fill([]);badArr[0].push(1);console.log(badArr);// [[1], [1], [1]] 所有子数组都变了⚠️ 核心坑点fill传入引用类型如数组、对象时填充的是引用地址所有位置共享同一个引用改一个全变✅ 正确创建二维数组的方式constarrnewArray(3);constlenarr.length;// 循环为每个位置创建新数组for(leti0;ilen;i){arr[i][];}arr[0].push(1);console.log(arr);// [[1], [], []] 仅第一个子数组变化三、数组操作纯函数vs非纯函数JS数组的方法分两类修改原数组非纯函数、返回新数组纯函数面试高频考点1. 非纯函数修改原数组方法作用返回值push尾部添加元素新数组长度pop尾部删除元素被删除的元素shift头部删除元素被删除的元素unshift头部添加元素新数组长度constarr[1,2,3];console.log(arr.push(4));// 4返回长度console.log(arr);// [1, 2, 3, 4]原数组被改console.log(arr.pop());// 4返回被删元素console.log(arr);// [1, 2, 3]原数组被改2. 纯函数不修改原数组方法作用返回值slice截取数组片段新数组concat拼接数组新数组map遍历映射新值新数组filter筛选符合条件元素新数组every检查所有元素是否符合条件布尔值some检查是否有元素符合条件布尔值reduce累计计算最终累计值constarr[1,2,3,4,5,6,7];// map映射新数组constdoubleArrarr.map(itemitem*2);console.log(doubleArr);// [2, 4, 6, 8, 10, 12, 14]console.log(arr);// 原数组不变// filter筛选元素constfilterArrarr.filter(itemitem3);console.log(filterArr);// [4, 5, 6, 7]// every所有元素是否符合条件console.log(arr.every(itemitem%20));// false不是所有数都是偶数// some至少一个元素符合条件console.log(arr.some(itemitem%20));// true有偶数// reduce求和constsumarr.reduce((pre,cur)precur);console.log(sum);// 28四、数组遍历5种方式性能对比遍历是数组最常用操作不同场景选不同方式性能和可读性天差地别1. for计数循环constarr[1,2,3,4,5];constlenarr.length;// 优化提前存长度避免每次访问arr.lengthfor(leti0;ilen;i){console.log(arr[i]);}✅ 优点性能最好底层最接近机器指令❌ 缺点可读性差命令式写法。2. for of循环constarr[1,2,3];for(constitemofarr){console.log(item);}✅ 优点语义化好简洁易读✅ 支持break/continue中途终止。3. forEach方法constarr[1,2,3];arr.forEach((item,index,self){console.log(item,index,self);// 元素、索引、原数组});✅ 优点功能强自带索引和原数组❌ 缺点不能用break/continue终止循环入调用栈性能略差。4. map方法constarr[1,2,3];constnewArrarr.map(itemitem*2);✅ 场景需要遍历并生成新数组❌ 注意不要用map做纯遍历浪费性能forEach更合适。5. 二维数组遍历constarrnewArray(3);constlenarr.length;// 初始化二维数组for(leti0;ilen;i){arr[i][];}// 双层遍历for(leti0;ilen;i){for(letj0;jlen;j){console.log(arr[i][j],i,j);}}⚠️ 优化点双层循环都提前存长度避免多次访问arr.length提升性能。五、实战场景这些坑90%的人都踩过坑1fill填充引用类型// 错误constarrnewArray(3).fill([]);arr[0].push(1);// 所有子数组都变了// 正确constarrnewArray(3);for(leti0;iarr.length;i){arr[i][];}坑2混淆push的返回值constarr[1,2];constresultarr.push(3);console.log(result);// 3是长度不是新数组console.log(arr);// [1, 2, 3]坑3forEach用break终止循环constarr[1,2,3];// 报错Uncaught SyntaxError: Illegal break statementarr.forEach(item{if(item2)break;console.log(item);});✅ 替代方案用for of循环或提前return仅终止当前迭代。六、总结JS数组看似简单实则藏着不少底层逻辑和实战坑创建数组时引用类型填充用循环别用fill操作数组时分清纯函数和非纯函数避免无意修改原数组遍历数组时性能优先选for计数可读性优先选for of生成新数组选map二维数组遍历要提前存长度优化性能。掌握这些知识点不管是日常开发避坑还是面试应对hot100题都能游刃有余。

更多文章