资讯动态

JSON数组操作全攻略:访问、遍历、嵌套、修改与删除

发布时间:2026/10/17 16:41:58 来源:尧图企业网站定制
1. 写在前面JSON 数组到底是个啥干这行久了你会发现JSON 几乎是无处不在的格式——接口返回的数据、前端渲染的列表、配置文件里的参数十有八九都是 JSON。而 JSON 里最常打交道、也最容易出问题的就是数组。不管你是刚入门的前端、后端还是做爬虫和数据清洗的访问数组值、遍历数组、处理嵌套数组、改数据、删数据这些操作几乎每天都在做。这个标题把 JSON 数组最常见的五个操作串在了一起访问数组值、遍历数组、处理嵌套数组、修改数组值、删除数组项目。看起来每个都是基础但实际写代码的时候坑全藏在这些“基础”里。比如索引越界、类型混淆、嵌套层级太深取不到值、删除元素后索引错位……这些坑我在实际项目里都踩过有的还踩了好几次。这篇文章就按这五个核心操作逐个拆解每个操作都讲清楚原理、给足示例、说透坑点。不管你是刚接触 JSON 的初学者还是写了几年业务代码的老手都建议完整过一遍很多细节值得回头确认一下。我们直接从 JSON 数组长什么样、怎么取到里面的值开始讲。2. 访问数组值索引规则与边界问题2.1 数组的“户口本”说清索引、元素、长度JSON 数组本质上就是一个有序列表用中括号[]包裹里面的元素可以是字符串、数字、布尔值、对象甚至另一个数组。我见过不少初学者把 JSON 数组和 JSON 对象搞混——数组用[]对象用{}这是最基础但最容易犯的错误。来看一个典型的 JSON 数组{ fruits: [apple, banana, cherry], scores: [78, 92, 85, 91], students: [ { name: A同学, age: 18 }, { name: B同学, age: 19 } ] }在这个 JSON 中fruits是一个字符串数组scores是数字数组students是对象数组。访问数组里的某个值核心就是“索引”。索引从 0 开始fruits[0]是applefruits[1]是banana最后一个元素的索引是数组长度 - 1。这就像排队第一个人的号牌是 0 而不是 1这个反直觉的设计让无数新手栽过跟头。长度用length属性获取。在 JavaScript 里直接访问jsonData.fruits.lengthPython 里用len()不同语言获取方式不同但“索引从 0 开始”这条规则是通用的。理解长度和索引的关系非常重要因为后面很多越界错误、删除错位问题根源都在这里。2.2 索引访问的实操细节与越界隐患先说 JavaScript 里的访问方式。假设我们拿到了一个 JSON 字符串第一步是先解析成对象const jsonStr {fruits: [apple, banana, cherry]}; const data JSON.parse(jsonStr); console.log(data.fruits[0]); // apple console.log(data.fruits[2]); // cherry console.log(data.fruits.length); // 3访问数组值最直接的坑就是“越界”。data.fruits[5]在 JavaScript 中不会报错而是返回undefined。这在调试时特别迷惑人——程序没有红字报错但结果就是不对。我早期排查过一个列表页面白屏的问题查了半天发现是某个索引越界返回了undefined后续代码还在对undefined的属性做访问直接抛异常了。Python 中的表现不一样越界会直接抛IndexErrorimport json data json.loads({fruits: [apple, banana, cherry]}) print(data[fruits][0]) # apple # print(data[fruits][5]) # IndexError: list index out of range这就引出一个实操心得访问数组值之前先确认数组是否为空、索引是否在有效范围内。我在业务代码里有一个习惯凡是索引来自外部传参比如取第几个这种需求都会加一个边界判断宁可多写两行防御代码也不让线上环境因为一个undefined白屏。另外一个常见误区是混淆“数组对象”和“JSON 字符串”。接口返回的原始数据往往是字符串字符串不能直接通过索引取元素必须先解析。很多新手直接data[0]结果拿到的是一个字符比如或者{整个逻辑就全乱了。还有字符串索引在多字节字符下按字节还是按字符取不同语言表现不一样这些细节虽然细碎但在处理文本类 JSON 数据时很容易踩雷。2.3 快速访问最后一个元素的小技巧实际开发中“取数组最后一个元素”的需求很常见。比如取最新一条日志、最近一条订单记录。最常见的写法是const lastFruit data.fruits[data.fruits.length - 1]; // cherry这个写法没问题但我还见过有人用data.fruits[-1]——这在 JavaScript 里是取不到最后一个元素的arr[-1]返回undefined。但是在 Python 里data[fruits][-1]反而能取到最后一个元素这是 Python 的语法特性。跨语言开发时这种差异非常容易导致“在 A 语言没问题、换到 B 语言就出错”的情况。JavaScript 里还有一个相对新的写法const lastFruit data.fruits.at(-1); // cherry.at(-1)在 ES2022 中引入写起来更语义化。不过考虑到部分旧环境兼容性问题我一般还是用arr[arr.length - 1]代码虽然多了点但兼容性稳。3. 遍历数组从 for 循环到链式迭代3.1 四种遍历方式对比与选择遍历数组是日常开发中频率最高的操作。JavaScript 里至少四五种遍历方式每种用在不同场景。我把常用方案整理成一个表格方便对比遍历方式适用场景能不能提前跳出能不能拿到索引注意点for循环几乎所有场景最通用可以break可以写法略繁琐需手动管理索引for...of只需要元素值可以break不能直接拿索引简洁清晰但无索引forEach全量遍历不需要中断不能break可以无法中止除非抛异常map需要生成新数组不可以中断可以必须有返回值否则产生undefined拿一个实际的 JSON 数据来演示{ employees: [ { name: 张三, salary: 8000, dept: 技术部 }, { name: 李四, salary: 9500, dept: 市场部 }, { name: 王五, salary: 12000, dept: 技术部 } ] }用for...of遍历员工的姓名const data JSON.parse(jsonStr); for (const emp of data.employees) { console.log(emp.name); }输出结果就是三个名字依次打印。如果是找第一个薪资超过 1 万的员工这种需求用for循环配合break最合适找到就停不用做无用功let target null; for (let i 0; i data.employees.length; i) { if (data.employees[i].salary 10000) { target data.employees[i]; break; } }forEach的坑在于“不能 break”。如果你在forEach里写了break直接语法报错。如果你用了return它不会真正跳出循环只是跳过当前一轮。这个行为经常被误认为“可以提前退出”实际上做不到。需要提前退出时用for或for...of更合适。我在重构旧代码时见过有人用forEach模拟中断效果用一个布尔标记配合if判断虽然能实现但代码可读性很差不如直接换循环。3.2 对象数组的解构与多层级取值遍历对象数组时经常需要同时拿多个字段。JavaScript 支持解构赋值可以省掉多余的点号访问for (const { name, salary } of data.employees) { console.log(${name} 的薪资是 ${salary}); }这种写法在遍历嵌套层级较深的数组时特别好用。比如数组元素里有嵌套对象{ orders: [ { id: 1001, customer: { name: 张三, level: VIP } }, { id: 1002, customer: { name: 李四, level: 普通 } } ] }访问客户姓名有两种路径// 方式一逐层访问 console.log(data.orders[0].customer.name); // 张三 // 方式二解构 for (const { id, customer: { name } } of data.orders) { console.log(id, name); }方式二看着炫但可读性略差。我个人的习惯是访问嵌套不超过两层的用逐层访问超过两层的先做中间变量赋值避免一长串.连到底。比如data.orders[0].customer.address.city这种三层以上的写法一旦中间某个环节是null或undefined整行代码直接报错排查还费劲。3.3 遍历时顺带做数据变换遍历不仅用于读取还经常用于“加工”。比如把员工薪资统一上涨 10%然后生成一个新数组const updated data.employees.map(emp ({ ...emp, salary: Math.round(emp.salary * 1.1) }));这个做法的核心是“不改原数据、生成新数组”。这种不可变的思想在 React 等框架中格外重要。直接改原数组也能实现但会破坏不可变性可能引发渲染问题。我实际开发中遇到过类似的案例某同事在遍历中直接修改了原始数组导致其他模块读取数据时拿到的值已经变了排查了两个小时才定位。所以这里强烈建议只要涉及“基于旧值生成新值”的操作优先考虑返回新数组的方案。4. 嵌套数组JSON 对象里的拆套神功4.1 嵌套数组的常见结构如果说单层数组是基本功那嵌套数组就是真正的日常考验。嵌套数组在真实业务里太常见了订单里有商品列表商品列表里又有规格选项文章有评论列表每条评论还有回复列表省份有城市城市有区县。基本就是“数组套对象、对象又套数组”的无限套娃。一个典型的嵌套 JSON 结构{ categories: [ { name: 电子产品, products: [ { title: 手机, specs: [6.1英寸, 128GB, 黑色] }, { title: 笔记本, specs: [14英寸, 512GB, 银色] } ] }, { name: 图书, products: [ { title: 编程入门, specs: [纸质书, 中文] } ] } ] }一共四层categories套对象、对象套products数组、products数组套对象、对象套specs数组。看起来层层叠叠但访问逻辑其实只有一条——分清你当前在哪个层级一层一层剥下去。4.2 逐层解套用路径思维理解访问过程访问“第一个分类下第二个商品的第二个规格”先别急着写代码先用路径来描述取categories的第 0 个元素 → 得到“电子产品”分类对象取其products的第 1 个元素 → 得到“笔记本”商品对象取其specs的第 1 个元素 → 得到512GB对应的代码就是const spec data.categories[0].products[1].specs[1]; console.log(spec); // 512GB可以看到嵌套数组的访问本质就是“一路索引下去”。只要每一步的索引和层级对得上就一定能取到值。怕就怕中间某一步的索引写错或者用错了属性名。我排查过不少 “为什么取到 undefined” 的问题最后发现是嵌套层级里少了一个products或者把specs写成了spec。所以我的建议是访问嵌套数组时优先用“路径拆解”的方式写代码。先把这个数据在纸上画出来标清楚每一层的结构再动手。尤其是接口返回的数据结构不明确时先console.log打印完整结构或者用在线 JSON 格式化工具看一下确认层级无误再写访问代码。别凭感觉写感觉在嵌套面前经常不可靠。4.3 嵌套遍历的工程化写法处理嵌套数组最常见的需求是“把所有商品的标题都取出来”。这就涉及双重遍历const titles []; for (const category of data.categories) { for (const product of category.products) { titles.push(product.title); } } console.log(titles); // [手机, 笔记本, 编程入门]如果层级再深一层比如把所有规格都拉出来就需要三重循环。循环层数越多代码越难看性能也越差。这时候我会用“扁平化”的思路把嵌套结构先打平成一维数组再处理。JavaScript 里可以用flatMapconst allProducts data.categories.flatMap(category category.products); const allTitles allProducts.map(p p.title); console.log(allTitles); // [手机, 笔记本, 编程入门]flatMap做了两件事先map再flat。如果嵌套层级不确定可以用flat(depth)比如data.categories.flat(2)展开两层。这种方法的优势是代码更短、逻辑更清晰而且天然避开了深层循环里的索引管理问题。Python 里也有类似的方案双重循环列表推导式titles [p[title] for c in data[categories] for p in c[products]]一行搞定了。但无论哪种语言处理嵌套数组的核心思维是一致的控制嵌套深度在清晰和简洁之间找平衡。我个人认为双重嵌套用循环没问题三重以上尽量打平或拆函数不然排查问题的时候你会在代码里绕晕。4.4 实战场景递归处理无限层级有些嵌套数组的层级是不固定的。比如菜单子菜单里可能还有子菜单评论区里回复还有回复。这种结构没法用固定层级的循环得用递归。一个简化的菜单结构{ menu: [ { name: 文件, children: [ { name: 新建 }, { name: 打开, children: [{ name: 最近打开 }] } ] }, { name: 编辑, children: [{ name: 复制 }] } ] }递归遍历所有的菜单名称function collectNames(items) { let names []; for (const item of items) { names.push(item.name); if (item.children item.children.length 0) { names names.concat(collectNames(item.children)); } } return names; } console.log(collectNames(data.menu)); // [文件, 新建, 打开, 最近打开, 编辑, 复制]递归的要点是先处理当前层再对子层调用自身同时一定要有终止条件这里是children不存在或为空。忘记终止条件的后果就是无限递归、栈溢出浏览器页面直接卡死。我在实际开发里写过一次没带终止条件的递归后果非常酸爽现在每写递归都会先想“这个递归会在什么条件下停下来”。无限层级的另一个常见坑是“隐藏的循环引用”——某个子节点指向了父节点导致递归死循环。如果数据来自不可控的外部接口建议加一个层级深度限制或者已访问记录安全第一。5. 修改数组值原地改还是重建5.1 直接赋值与索引替换修改数组里某个位置的值最直接的方法是“索引定位 重新赋值”。// 把第一个水果改成 pear data.fruits[0] pear; console.log(data.fruits); // [pear, banana, cherry]这个操作简单直接但有个前提数组本身是可变的。在 JavaScript 中数组天生可变所以直接改没有问题。在 Python 里同样data[fruits][0] pear索引替换适合“知道要改哪个位置”的场景。但真实业务里更多情况是“知道条件、不知道位置”。比如“找到薪资 8000 的员工把薪资改成 8500”这就不能靠硬编码索引了得先遍历找位置再改。for (let i 0; i data.employees.length; i) { if (data.employees[i].name 张三) { data.employees[i].salary 8500; break; } }这里有个值得注意的点修改的是对象的属性不是数组元素本身。data.employees[i]拿到的是对象引用改它的属性相当于直接改原对象。这种方式在很多场景下是可控的但如果你想替换整个对象还是要写data.employees[i] { newObject }不能只改属性。5.2 新增元素push、unshift 与 concat往数组里加元素有三种常见方式各有用处方法作用影响位置修改原数组push()在末尾追加末尾是unshift()在开头插入开头是concat()或展开运算符拼接生成新数组取决于写法否末尾追加是最常见的data.fruits.push(orange); console.log(data.fruits); // [pear, banana, cherry, orange]开头插入用的相对少但偶尔会遇到data.fruits.unshift(grape); console.log(data.fruits); // [grape, pear, banana, cherry, orange]unshift有一个性能隐患数组很大时每次从头插入都要把所有元素往后移时间复杂度高。如果需要在开头频繁插入大量数据建议换一种数据结构或者在最后统一处理别硬刚。不可变的方式是用展开运算符生成新数组const newFruits [...data.fruits, orange]; // 末尾加 const anotherNewFruits [grape, ...data.fruits]; // 开头加实际代码里选择哪种方式取决于业务需求。如果只是临时改一下变量push就够如果涉及状态管理比如 React 的useState一定要用不可变方式直接改原数组再赋回同一个引用界面不会刷新这个问题我在 React 项目里见了不止一次。5.3 批量修改map 与 forEach 的取舍批量修改是另一个高频需求。比如“所有技术部员工的薪资上涨 5%”。两个方案供你对比方案一forEach直接改原数组data.employees.forEach(emp { if (emp.dept 技术部) { emp.salary Math.round(emp.salary * 1.05); } });这个方案直观直接改了原数组后续所有引用到这个数组的代码拿到的都是更新后的值。方案二map生成新数组const updated data.employees.map(emp { if (emp.dept 技术部) { return { ...emp, salary: Math.round(emp.salary * 1.05) }; } return emp; });方案二不会改动data.employees原数组而是返回一个全新的数组。我强烈建议在组件化开发中用方案二。你永远不知道哪些地方还在引用旧数组直接改原数组可能导致无法预期的副作用。我之前做一个列表页时某同事在“批量调整”功能里直接改了原数组结果另一个只读组件用同一个数据源渲染列表用户一点批量调整那边的列表也跟着变了排查了老半天才找到原因。5.4 修改嵌套数组的内部元素嵌套数组的修改比单层要小心常见需求是“把某个分类下所有商品的价格打折”。核心思路和之前访问嵌套一样先走路径找到对应的数组再修改。// 给电子产品分类下所有商品打 8 折 for (const category of data.categories) { if (category.name 电子产品) { for (const product of category.products) { product.price Math.round(product.price * 0.8); } break; } }这种多层修改最怕的是引用出错。我说个真实经历当时的数据结构是“分类套商品、商品套规格”我给商品加了一个新字段但只更新了第一层循环里的商品对象忘了规格里的某些汇总数据也要同步结果页面上显示的商品规格数量不一致。所以修改嵌套数组时多想想“这个改动是否需要连带修改其他层级”结构越深牵一发动全身的概率越大。6. 删除数组项目splice、filter 与 delete 的三国演义6.1 三兄弟的本质区别删除数组项目JavaScript 里至少有三种做法但它们的实现逻辑和后果完全不同。我把三兄弟放在一起对比方法语法修改原数组遗留空位典型用途splice(index, count)从指定位置删 N 个是否数组会收缩按索引删除filter(回调)返回满足条件的新数组否否生成新数组按条件筛选删除delete arr[index]删除某个位置的元素是是留下empty几乎不用最关键的区别在于delete和三兄弟中的另外两个。delete会把那个位置的元素删除但数组的长度不变那个位置变成一个“空洞”。在 JavaScript 中访问这个空洞的位置返回undefined遍历时会跳过它。看起来无所谓但如果你对这个数组做map或JSON.stringify结果会很意外——序列化时空洞位置会变成null。我在一次接口对接中遇到过这个问题前端删了一个元素传给后端的 JSON 里出现了一堆null导致后端解析异常。从那以后我就很少在业务代码里用delete处理数组元素。6.2 按索引删除splice 的正确姿势如果已经知道了要删除元素的索引splice是最典型的方案// 删除cherry它在索引 2 data.fruits.splice(2, 1); console.log(data.fruits); // [pear, banana, orange]splice的第二个参数是删除个数很多初学者写成splice(2)结果把索引 2 开始的所有元素全部删掉了剩下的数组空荡荡的。记住第二个参数必须写哪怕只需要删一个。这个坑我踩过一次也亲眼看着同事踩过一次代价都是重新拉数据。除了解除错位的索引另一个常见场景是“删除后还要在这个位置插入新元素”。splice支持后接参数插入// 把索引 1 的 banana 替换为 blueberry data.fruits.splice(1, 1, blueberry); console.log(data.fruits); // [pear, blueberry, orange]删除和插入一步到位代码紧凑。但要注意如果删掉一个元素后再用原索引访问后续元素索引已经整体前移了。比如原来fruits[3]是orange删了fruits[2]之后fruits[3]就变成undefined原来的orange跑到了fruits[2]。这就是“索引错位”在循环里删除项目时最容易遇到。6.3 循环删除的经典坑索引错位先看一段出问题的代码const numbers [1, 2, 2, 3, 4]; // 删除所有等于 2 的元素 for (let i 0; i numbers.length; i) { if (numbers[i] 2) { numbers.splice(i, 1); } } console.log(numbers); // 猜猜结果[1, 2, 3, 4]结果里还有一个 2原因就是索引错位第一次删除索引 1 的2之后原索引 2 的2移动到索引 1但循环的i已经加到 2 了这个2就被跳过了。解决思路有三种方法一倒序遍历for (let i numbers.length - 1; i 0; i--) { if (numbers[i] 2) { numbers.splice(i, 1); } }倒序时删除当前元素只会影响已处理过的索引不会影响未来要判断的索引。这是最简单有效的方案不需要额外的变量。方法二使用filterconst cleaned numbers.filter(n n ! 2);filter直接生成新数组不需要处理索引问题代码最简洁。这也是我日常最推荐的方案——能用filter表达的逻辑不要手动管理索引。方法三每次删除后回退索引for (let i 0; i numbers.length; i) { if (numbers[i] 2) { numbers.splice(i, 1); i--; } }i--让循环在删除后重新检查当前位置也能解决问题但需要额外维护索引不如倒序直观。6.4 按条件删除filter 的妙用按条件删除数据是业务里最常见的。比如“删除所有年龄小于 18 的员工”const adults data.employees.filter(emp emp.age 18);filter的回调函数返回true则保留返回false则过滤掉。这种写法的优势非常明显不会修改原数组逻辑是声明式的——你描述“保留什么”而不是“删掉哪个位置”。代码的可读性远高于splice加循环的组合。但要注意filter永不修改原数组如果你确实需要修改原数组比如原数组被多个地方引用你希望所有引用方都能看到删除结果那就要给filter的结果重新赋值data.employees data.employees.filter(emp emp.age 18);重新赋值后原来的数组如果还有其他变量引用那些引用拿到的是旧数组。这个问题就像修改数组值时说的不可变性一样要清楚每种方案是“原地改”还是“重建引用”搞明白这一点能省掉很多排查时间。嵌套数组的删除更复杂一点。比如“删除某个分类下所有价格为 0 的规格”。这时候要在嵌套循环里对子数组做过滤for (const category of data.categories) { category.products category.products.filter(p p.price 0); }因为filter返回新数组必须通过赋值把新数组放回原位置否则过滤结果没有保存白做了。这个细节非常容易忽略我见过不少同事写了filter但没赋值然后疑惑“为什么数据没变”。7. 高频报错与排查经验记录7.1 最常见的五个报错与应对把这几年的经验沉淀一下我整理了 JSON 数组操作中最高频的五个报错场景以及对应的排查思路现象根本原因排查方向访问数值返回undefined索引越界或路径写错先打印整个数组确认长度和结构Cannot read property xx of undefined嵌套路径中间层是undefined分层打印找到第一个出问题的层级循环删除后仍有残留元素正序遍历时索引错位改为倒序遍历或用filterJSON.stringify输出为null数组用delete留下空洞避免对数组用delete改用splice字符串被当成数组处理忘记JSON.parse确认变量类型必要时Array.isArray判断最后一个问题在初学者中特别普遍。后端返回的是 JSON 字符串前端忘了JSON.parse直接调用.map浏览器直接抛出“.map is not a function”。看到这个报错第一反应就是检查这个变量到底是不是数组用Array.isArray()一测便知。7.2 如何快速定位嵌套数组的问题嵌套数组出问题时最忌讳“靠猜”。我每次排查这类问题都有一套固定流程按步骤走基本十分钟内能定位第一步console.log(JSON.stringify(data, null, 2))打印完整数据肉眼确认结构长什么样需要复现的层级是否真的存在。第二步逐层拆解每次只访问一层。比如先把data.categories打出来看看是不是数组再打data.categories[0]再打data.categories[0].products逐层推进直到找到断点。第三步写一个防御性的访问函数。JavaScript 中可以用可选链?.const spec data?.categories?.[0]?.products?.[1]?.specs?.[1]; console.log(spec);可选链在中间任何一层为null或undefined时不会抛错而是返回undefined。结合??还可以给默认值const spec data?.categories?.[0]?.products?.[1]?.specs?.[1] ?? 默认值;这个写法非常适合处理外部接口数据因为接口结构可能随时调整防御性访问能避免线上崩溃。但要注意可选链不能滥用它只是“不报错”不是“结果对”——如果data本身就是空对象它返回的永远是undefined这时候还要配合对返回值的判断一起用。7.3 深拷贝与数据污染处理 JSON 数组时还有个容易忽略的问题引用共享。比如把一个数组赋值给新变量const newArr data.fruits; newArr.push(kiwi); console.log(data.fruits); // 原数组也变了因为newArr和data.fruits指向同一个数组对象改一个等于改两个。很多初学者期望“复制一份再改”结果在原数据上动了刀这就是引用共享导致的“数据污染”。如果需要真正的独立副本用浅拷贝或深拷贝const newArr [...data.fruits]; // 浅拷贝 const deepCopy JSON.parse(JSON.stringify(data)); // 深拷贝浅拷贝对一维数组没问题但数组里是对象时拷贝的是对象引用——修改内部对象属性原数组也会跟着变。需要完全独立时用JSON.parse(JSON.stringify(data))做深拷贝。这种方法简单粗暴但如果数据里有函数、undefined、循环引用它会丢失这些内容不适合所有场景。也可以用结构化克隆方法structuredClone在较新环境下支持得不错兼容性要按实际运行环境评估。深拷贝在修改嵌套数组时尤其重要。你不想“改副本却动了原数据”也不想“改原数据却以为自己只改了副本”。我在写一个配置编辑器时专门做了一个“先深拷贝后编辑最后保存”的流程彻底避开了这类问题。8. 写在最后的实操心得8.1 如何处理接口返回的极端数据实际项目里接口返回的 JSON 数组比我们想象的更容易出幺蛾子数组为空、某个字段缺失、某个元素是null、甚至整个数据直接是null而不是数组。我处理外部接口数据时有个习惯——永远假设数据是不完整的。我会写一个专门的数据校验函数入口先检查类型function processEmployees(data) { if (!Array.isArray(data?.employees)) { console.warn(employees 不是数组或不存在返回空数组); return []; } // 正常处理逻辑 }这种“门卫式”的校验在接口不可控时非常可靠。宁可多写几行防御代码也不要让页面直接白屏。实际开发中白屏就是事故多写的这几行代码就是保险。8.2 性能与可读性的平衡处理大数组时性能问题会被放大。一个一万条数据的数组用for循环和用map性能差异不算太大但如果嵌套三层、每层一万条暴力循环可能达到几秒甚至更久。这时候我会考虑避免在循环里做不必要的JSON.stringify避免在循环里频繁操作 DOM前端场景无法避免嵌套全遍历时考虑分页、懒加载但大部分业务场景数据量远没到需要极致优化的程度可读性和可维护性比那几毫秒更重要。代码是写给人看的不只是写给机器执行的。把复杂的遍历逻辑拆成小函数把魔法索引改成常量这些习惯比“多快那么一点点”更有价值。8.3 最后再分享一个小技巧每次拿到一个未知结构的 JSON 数据我第一件事就是格式化并打印。浏览器、编辑器、在线工具都行但一定要把完整结构看清楚再动手。很多人出问题归根结底就一句话没看清数据结构就开始写代码。看清楚了再复杂的嵌套数组也就那么回事——一步一步走一层一层剥总能拿到你要的值。JSON 数组的操作没有太多高深的东西但细节决定体验。希望这篇整理能帮你少踩几个坑尤其是索引错位、引用共享、嵌套层级这三座大山跨过去了后面就越写越顺。如果读的时候手边有编辑器建议把这几个例子都敲一遍自己的记忆比别人说的任何结论都可靠。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑