JavaScript 展开运算符(Spread Operator)完全指南 JavaScript 展开运算符Spread Operator完全指南深入理解...的语法、行为和各种边界情况目录一、基础概念二、对象展开三、数组展开四、条件展开详解五、对象 vs 数组的行为差异六、常见坑点与最佳实践七、完整参考表一、基础概念展开运算符...允许一个表达式在期望多个元素数组或多个参数函数调用或多个属性对象的位置展开。两种使用场景javascript// 1. 对象展开 - 复制对象属性 const obj { ...source }; // 2. 数组展开 - 展开可迭代元素 const arr [...source];二、对象展开2.1 基本用法javascript// 复制对象 const source { name: 张三, age: 18 }; const copy { ...source }; // 结果{ name: 张三, age: 18 } // 合并对象 const obj1 { a: 1, b: 2 }; const obj2 { c: 3, d: 4 }; const merged { ...obj1, ...obj2 }; // 结果{ a: 1, b: 2, c: 3, d: 4 } // 覆盖属性后面的覆盖前面的 const base { name: 张三, age: 18 }; const update { age: 20, city: 北京 }; const result { ...base, ...update }; // 结果{ name: 张三, age: 20, city: 北京 }2.2 各种值的展开行为javascript// ✅ 普通对象 const obj { name: 张三 }; { ...obj } // { name: 张三 } // ✅ 空对象 const empty {}; { ...empty } // {} // ✅ 数组展开为对象索引变属性名 const arr [1, 2, 3]; { ...arr } // { 0: 1, 1: 2, 2: 3 } // ✅ 字符串展开为对象 const str abc; { ...str } // { 0: a, 1: b, 2: c } // ✅ null 和 undefined被忽略 { ...null } // {} 不报错 { ...undefined } // {} 不报错 // ✅ 数字和布尔值 { ...123 } // {} 数字没有可枚举属性 { ...true } // {} 布尔没有可枚举属性 // ❌ 注意在对象字面量外部不能直接展开 // ...null; // 语法错误2.3 对象展开的规范行为根据 ECMAScript 规范对象展开时null或undefined直接跳过不处理原始类型string、number、boolean先包装成对象再复制可枚举属性普通对象复制所有可枚举属性javascript// 规范示例 const obj { ...null, // 跳过 ...undefined, // 跳过 ...abc, // { 0: a, 1: b, 2: c } ...123, // {} 无属性 ...true, // {} 无属性 ...{ x: 1 } // { x: 1 } }; // 最终结果{ 0: a, 1: b, 2: c, x: 1 }三、数组展开3.1 基本用法javascript// 复制数组 const source [1, 2, 3]; const copy [...source]; // 结果[1, 2, 3] // 合并数组 const arr1 [1, 2]; const arr2 [3, 4]; const merged [...arr1, ...arr2]; // 结果[1, 2, 3, 4] // 添加元素 const arr [1, 2]; const newArr [0, ...arr, 3]; // 结果[0, 1, 2, 3]3.2 各种值的展开行为javascript// ✅ 普通数组 const arr [1, 2, 3]; [...arr] // [1, 2, 3] // ✅ 空数组 const empty []; [...empty] // [] // ✅ 字符串可迭代 const str abc; [...str] // [a, b, c] // ✅ Set可迭代 const set new Set([1, 2, 3]); [...set] // [1, 2, 3] // ✅ Map可迭代返回键值对数组 const map new Map([[a, 1], [b, 2]]); [...map] // [[a, 1], [b, 2]] // ❌ null 和 undefined报错 [...null] // TypeError: null is not iterable [...undefined] // TypeError: undefined is not iterable // ❌ 普通对象不可迭代 const obj { name: 张三 }; [...obj] // TypeError: obj is not iterable // ❌ 数字不可迭代 [...123] // TypeError: 123 is not iterable // ❌ 布尔值不可迭代 [...true] // TypeError: true is not iterable3.3 数组展开的要求数组展开要求值必须是可迭代对象Iterable✅ Array✅ String✅ Set✅ Map✅ NodeListDOM 元素集合✅ arguments 对象❌ 普通对象❌ null / undefined❌ Number / Boolean四、条件展开详解4.1 核心语法javascript...(condition { data })这是短路求值展开运算符的组合使用。4.2 执行步骤javascript// 代码 ...(data { data }) // 步骤1计算 data { data } // 如果 data 是真值 → 返回 { data } // 如果 data 是假值 → 返回 data 本身 // 步骤2展开步骤1的结果 // 如果是对象 → 展开属性 // 如果是假值null/undefined→ 在对象中忽略4.3 各种值的行为data 值data { data }结果展开结果{ id: 1 }{ data: { id: 1 } }✅ 添加 data 字段[1, 2, 3]{ data: [1, 2, 3] }✅ 添加 data 字段hello{ data: hello }✅ 添加 data 字段true{ data: true }✅ 添加 data 字段nullnull⏭️ 被忽略undefinedundefined⏭️ 被忽略00⏭️ 被忽略⚠️ 可能是问题⏭️ 被忽略⚠️ 可能是问题falsefalse⏭️ 被忽略⚠️ 可能是问题NaNNaN⏭️ 被忽略⚠️ 可能是问题4.4 实际应用示例javascript// 当前代码 function sendJsonResponse(res, statusCode, message, data null) { return res.status(statusCode).json({ message, ...(data { data }) }); } // 测试 sendJsonResponse(res, 200, 成功, { id: 1 }); // 返回{ message: 成功, data: { id: 1 } } sendJsonResponse(res, 200, 成功, null); // 返回{ message: 成功 } ✅ 干净没有 data: null sendJsonResponse(res, 200, 成功, 0); // 返回{ message: 成功 } ⚠️ 但你可能想要 data: 04.5 ⚠️ 注意假值陷阱javascript// 问题0、、false 是有效数据但会被当作无数据 const data 0; ...(data { data }) // 不会添加 data 字段 // 解决方案1只排除 null 和 undefined ...(data ! null { data }) // 或 ...(data ! null data ! undefined { data }) // 解决方案2使用空值合并运算符 ...(data ?? { data }) // 注意这会在 data 为 null/undefined 时添加 {} // 但这样会添加空对象不推荐五、对象 vs 数组的行为差异5.1 核心区别特性对象展开{...}数组展开[...]null 和 undefined✅ 被忽略不报错❌ 报错不可迭代普通对象✅ 复制属性❌ 报错不可迭代数组✅ 索引变属性名✅ 展开元素字符串✅ 索引变属性名✅ 展开字符数字/布尔✅ 无属性不报错❌ 报错不可迭代Set/Map❌ 报错普通对象行为✅ 展开元素5.2 对比示例javascript// 对象展开 - 宽容 const objResult { ...null, // ✅ 跳过 ...undefined, // ✅ 跳过 ...abc, // ✅ { 0: a, 1: b, 2: c } ...[1, 2], // ✅ { 0: 1, 1: 2 } ...123, // ✅ {} }; // 结果{ 0: 1, 1: 2, 2: c } // 数组展开 - 严格 try { const arrResult [ ...null, // ❌ TypeError ...undefined, // ❌ TypeError ...abc, // ✅ a, b, c ...[1, 2], // ✅ 1, 2 ...123, // ❌ TypeError ]; } catch (e) { console.log(数组展开报错); }5.3 安全的数组条件展开javascript// 方案1使用空数组默认值 const arr [...(data ?? [])]; // 方案2先判断再展开 const arr data ? [...data] : []; // 方案3Array.from更安全 const arr Array.from(data ?? []); // 方案4concat const arr [].concat(data ?? []); // 方案5条件判断 const arr []; if (data Array.isArray(data)) { arr.push(...data); }六、常见坑点与最佳实践6.1 坑点清单❌ 坑1假值被忽略javascript// 问题 const data 0; ...(data { data }) // data 字段丢失 // 解决 ...(data ! null { data })❌ 坑2数组展开 nulljavascript// 问题 const arr [...data]; // data 为 null 时报错 // 解决 const arr [...(data ?? [])];❌ 坑3对象展开非普通对象javascript// 问题 const obj { ...new Date() }; // 会复制日期对象的所有属性 // 结果{ toString: [Function], toDateString: [Function], ... } // 解决明确只复制需要的属性 const { year, month } new Date();❌ 坑4浅拷贝问题javascript// 问题展开只做浅拷贝 const original { user: { name: 张三 } }; const copy { ...original }; copy.user.name 李四; console.log(original.user.name); // 李四被修改了 // 解决深拷贝 const deepCopy JSON.parse(JSON.stringify(original)); // 或使用 structuredCloneNode 17 const clone structuredClone(original);6.2 最佳实践✅ 推荐1条件展开只排除 null/undefinedjavascript// 好的做法 ...(data ! null { data }) // 而不是 ...(data { data })✅ 推荐2数组展开使用空值合并javascript// 好的做法 const arr [...(items ?? [])]; // 而不是 const arr [...items]; // items 为 null 时报错✅ 推荐3对象展开保持纯净javascript// 好的做法使用展开创建新对象 const updateUser (user, updates) ({ ...user, ...updates, updatedAt: new Date() }); // 而不是直接修改原对象 const updateUser (user, updates) { Object.assign(user, updates); // ❌ 修改了原对象 return user; };✅ 推荐4使用展开进行不可变更新javascript// 添加属性 const newState { ...state, newProp: value }; // 更新属性 const newState { ...state, prop: newValue }; // 删除属性 const { deleteProp, ...newState } state; // 更新嵌套属性 const newState { ...state, user: { ...state.user, age: 20 } };七、完整参考表7.1 对象展开参考表值示例结果是否报错普通对象{ ...{a:1} }{ a: 1 }✅ 否空对象{ ...{} }{}✅ 否数组{ ...[1,2] }{ 0: 1, 1: 2 }✅ 否字符串{ ...abc }{ 0: a, 1: b, 2: c }✅ 否null{ ...null }{}✅ 否undefined{ ...undefined }{}✅ 否数字{ ...123 }{}✅ 否布尔值{ ...true }{}✅ 否Set{ ...new Set() }{}✅ 否Date{ ...new Date() }复制所有属性✅ 否7.2 数组展开参考表值示例结果是否报错数组[...[1,2]][1, 2]✅ 否空数组[...[]][]✅ 否字符串[...abc][a, b, c]✅ 否Set[...new Set([1,2])][1, 2]✅ 否Map[...new Map([[a,1]])][[a, 1]]✅ 否null[...null]-❌ TypeErrorundefined[...undefined]-❌ TypeError普通对象[...{a:1}]-❌ TypeError数字[...123]-❌ TypeError布尔值[...true]-❌ TypeError7.3 条件展开参考表条件值表达式对象展开结果数组展开结果{ data: 1 }...(value { data })✅ 添加 data❌ 报错null...(value { data })⏭️ 忽略❌ 报错undefined...(value { data })⏭️ 忽略❌ 报错0...(value { data })⏭️ 忽略❌ 报错...(value { data })⏭️ 忽略❌ 报错false...(value { data })⏭️ 忽略❌ 报错八、总结对象展开对null/undefined宽容在对象字面量中会被静默忽略数组展开要求值必须可迭代null/undefined会直接报错条件展开...(data { data })是一种优雅的条件添加属性的方式注意假值陷阱0、、false会被当作无数据推荐使用data ! null来精确判断是否添加字段javascript// 最终推荐写法 function sendJsonResponse(res, statusCode, message, data null) { return res.status(statusCode).json({ message, ...(data ! null { data }) // 只有 null/undefined 时不添加 }); }参考资料MDN: Spread syntaxECMAScript SpecificationJavaScript 展开运算符的深度解析 本文由 [小新1688] 整理欢迎分享和讨论如果觉得有用请给个 Star ⭐ 或分享给更多开发者~