- 教程
【免费下载链接】jstips
This is about useful JS tips!
导读
本篇基于 jstips 仓库第 43 期技巧(对应文件 _posts/es_ES/javascript/2016-02-12-use-destructuring-in-function-parameters.md,英文版见 _posts/en/javascript/2016-02-12-use-destructuring-in-function-parameters.md),讲解如何在函数参数位置直接使用 ES6 解构赋值(Destructuring Assignment),让"接收 options 对象"的函数签名变得声明式、可读、自解释。读完你将掌握:参数级解构的写法、与默认参数(Default Parameters)的组合技巧、参数缺失/类型错误等边界情况的正确处理,以及它在 Node.js 中的可用性与历史限制。
参数解构:把对象参数直接"拆开"
很多开发者早已熟悉 ES6 的解构赋值语法——它让我们可以从数组或对象中一次性提取多个值。但鲜为人知的是,解构语法同样可以直接写在函数的参数列表里:
var sayHello = function({ name, surname }) { console.log(`Hello ${name} ${surname}! How are you?`); }; sayHello({ name: 'John', surname: 'Smith' }) // -> Hello John Smith! How are you?当调用方传入{ name: 'John', surname: 'Smith' }时,参数列表中的{ name, surname }会立即把该对象的name与surname两个属性"拆"成两个局部变量,函数体内部无需再写params.name、params.surname。
这种写法最典型的应用场景,就是接收一个 options/配置对象的函数:函数签名直接暴露它需要的字段,而不是一个不透明的options对象,调用方一眼就能看出契约。
叠加默认参数:调用方留空的字段自动填充
对于"可选项很多、调用方经常只传其中一部分"的 options 对象场景,可以让解构与默认参数协同工作,把调用方省略(或忘传)的字段自动填充为默认值:
var sayHello2 = function({ name = "Anony", surname = "Moose" } = {}) { console.log(`Hello ${name} ${surname}! How are you?`); };逐段拆解这个签名:
{ name = "Anony", surname = "Moose" }:声明解构目标对象,并对name、surname两个属性分别给出默认值;= {}:为整个解构参数声明默认值为空对象{}——当调用方完全没传参数,或传入了错误类型(见下文"参数处理")时,就改用{}作为解构来源,从而避免运行时崩溃。
实际调用效果:
sayHello2() // -> Hello Anony Moose! How are you? sayHello2({ name: "Bull" }) // -> Hello Bull Moose! How are you?注意一个细节:sayHello2({ name: "Bull" })只传了name,此时surname属性缺失,便由默认值"Moose"顶上。这正是"属性级默认值"与"整体对象默认值"两层防护的配合。
参数处理:边界情况与防御性写法
普通解构下的undefined泄漏
如果不使用任何默认值,当传入对象缺少函数签名中声明的字段时,未匹配到的参数一律为undefined,需要调用方自行兜底:
var sayHelloTimes = function({ name, surname }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); } sayHelloTimes({ name: "Pam" }, 5678) // -> Hello Pam undefined! I've seen you 5678 times before. sayHelloTimes(5678) // -> Hello undefined undefined! I've seen you undefined times before.整个参数缺失:直接抛 TypeError
更糟糕的情况是:需要被解构的那个参数根本没传。此时解构无从谈起,函数直接抛出异常,可能导致整个应用崩溃:
sayHelloTimes() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'...从概念上讲,这类似于访问一个未定义对象的属性——都是对undefined/null取值,只是异常类型不同。这提醒我们:只要函数签名中有解构参数,就必须考虑"一个参数都不传"的调用路径。
属性默认值 + 对象默认值:双保险
为解构参数同时加上属性级默认值和整体= {}默认值,可以把上述问题大部分掩盖掉:
var sayHelloTimes2 = function({ name = "Anony", surname = "Moose" } = {}, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2() // -> Hello Anony Moose! I've seen you undefined times before.对照三种调用可以看到:= {}覆盖了"整个对象缺失"的情形,而属性默认值覆盖了"对象存在但个别属性缺失"的情形——两者职责互补。
只加属性默认值、不加对象默认值:仍会崩溃
如果把整体= {}去掉,只保留属性级默认值,那么"对象缺失"这一层防护就没了:
var sayHelloTimes2a = function({ name = "Anony", surname = "Moose" }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2a({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2a(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2a() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'.从最后一行可以看到,sayHelloTimes2a()依旧抛出TypeError。原文档的结论非常明确:属性级默认值只对"属性缺失"负责,只有= {}这类整体对象默认值才能兜住"对象本身缺失"的极端调用。
可用性与历史限制
需要注意,解构赋值并非在写作时(2016 年前后)的所有运行环境中默认可用:
- 在当时的 Node.js 中,解构赋值可能默认未开启。若想提前体验,可在启动时显式传入 Harmony 标志:
node --harmony-destructuring your_script.js- 在浏览器端,当时的支持情况也参差不齐,建议实际部署前先在目标运行环境验证。
上述"可用性"信息以原文档写作时(tip 43,对应 2016 年 2 月)为背景。如今的主流 Node.js 版本与浏览器早已原生支持参数解构与默认参数,
--harmony-destructuring标志仅用于理解这段演进历史。对于现代运行环境,请以当前环境的实测结果为准。
与仓库内其他 ES6 技巧的呼应
参数解构与默认参数是 ES6 参数体系的两块基石,本仓库中还有几期技巧与之直接相关,建议对照阅读:
- Tip 12「ES6 函数中的伪必填参数」(_posts/en/javascript/2016-01-12-pseudomandatory-parameters-in-es6-functions.md):利用默认参数"未传值时执行默认表达式"的特性,让
_err(...)这类抛错函数成为默认值,从而把参数从"可选的"变成"伪必填的"。这与本期的"属性默认值填充"形成互补——默认值既可以兜底,也可以主动报错。 - Tip 44「了解传值机制」(_posts/en/javascript/2016-02-13-know-the-passing-mechanism.md):理解对象参数是"按引用值传递(call-by-sharing)",有助于把握解构拿到的是引用还是副本,避免在解构后误改调用方对象。
- Tip 54「如何在函数中使用可选参数(含可选回调)」(_posts/en/javascript/2016-05-06-use-optional-arguments.md):展示了不用解构时,通过
arguments/剩余参数手动解析可选参数的传统写法;与本期写法对比,更能体会参数解构在可读性上的优势。
此外,本期技巧在仓库中还有 简体中文版 与 繁体中文版,可作为双语对照阅读。
小结
一句话概括本期技巧:当函数需要接收 options 对象时,直接在参数列表里解构它——配合属性级默认值与= {}整体默认值,就能用最少的代码写出既自解释、又对边界情况健壮(不传参、少传属性、传错类型)的函数签名。撰写健壮的库 API 时,这套"参数解构 + 双层默认值"的组合是值得优先采用的模式。
- 教程
【免费下载链接】jstips
This is about useful JS tips!
相关推荐
jstips 第 43 期:ES6 函数参数解构(Destructuring in Function Parameters)完整实战指南
jstips 第 43 期:ES6 函数参数解构(Destructuring in Function Parameters)完整实战指南 本篇对应 jstips
教程jstips 第 12 期:ES6 伪强制参数(Pseudomandatory Parameters)——用默认参数实现必传校验
jstips 第 12 期:ES6 伪强制参数(Pseudomandatory Parameters)——用默认参数实现必传校验 导读 在 JavaScript
教程如何快速上手LibVMI:5分钟搭建虚拟化内省开发环境
如何快速上手LibVMI:5分钟搭建虚拟化内省开发环境 LibVMI是一款强大的虚拟化内省工具,能够让开发者直接访问和分析虚拟机的内存与状态,是系统安全研究、调
虚拟化网络安全操作系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考