A05 函数与参数

2026-10-31 17:250 阅读约 11 分钟JavaScript
AI 摘要30 秒读完

本文讲解JavaScript函数定义方式、参数设计规则与返回值约定,帮助开发者写出契约清晰、可测试的函数。

  • 函数调用经历建立上下文、绑定参数、执行体、返回结果四个阶段,参数不足不报错。
  • 四种函数定义方式各有适用场景,箭头函数继承外层this,适合回调与工具函数。
  • 参数超过三个改用对象参数,可选参数给默认值,非法输入在入口立即抛错。
  • 默认值只对undefined生效,传null不会触发默认值,需调用侧用??兜底。
  • 核心逻辑写成纯函数,函数不修改入参,副作用留在边界处理。
AI 生成 · 仅供参考

A05 函数与参数

摘要:函数把输入映射为输出,是复用与测试的最小单位。参数传递只有一种语义:原始类型传值,对象传引用;默认参数、剩余参数与解构能显著减少调用侧的样板代码。本文给出函数声明与表达式的取舍、参数设计规则、返回值约定与纯函数判定方法。
关键词:函数,参数,默认值,剩余参数,纯函数

所属:frontend/js / A-入门 | 上一篇:A04-条件分支与循环 | 下一篇:A06-数组与对象

1 引言

同一个功能写成三个函数时,缺陷通常不在语法而在契约:参数是必填还是可选、返回值是原始值还是对象、出错时返回 null 还是抛异常。契约不清晰,调用方只能读实现。

本文解决三个问题:函数怎么写、参数怎么设计、返回值怎么约定。范围见表 1-1。

项 本文覆盖 本文不覆盖
定义 声明、表达式、箭头函数、立即执行 构造函数与 new(B02)
参数 默认值、剩余参数、解构、对象参数 柯里化与函数组合(B01)
返回值 单一出口、早返回、纯函数判定 异步函数(B03)

2 预备知识

项 要求 核对方式
环境 Node.js 20+ 或浏览器控制台 能执行多行脚本
前置 A04 能写分支与循环
文件 js-practice/functions.js 编码 UTF-8

3 原理与实现

3.1 调用时的四项动作

一次调用按「建立执行上下文 → 绑定参数 → 执行函数体 → 返回结果」进行,如图 3-1 所示。

flowchart TD
    A["调用 fn(a, b)"] --> B["创建新的执行上下文"]
    B --> C["形参绑定:缺省用默认值,多余进 rest"]
    C --> D["执行函数体"]
    D --> E{"遇到 return"}
    E -->|是| F["返回该值并结束"]
    E -->|无 return| G["返回 undefined"]
    F --> H["调用方继续"]
    G --> H

图 3-1 一次函数调用的四个阶段

图上可见:参数不足不会报错,未传的形参为 undefined;只有显式 return 才有返回值,漏写在链式调用中会得到 undefined。

3.2 完整实现

// js-practice/functions.js;运行环境:Node.js 20+
// 1 对象参数:超过两个参数时改用解构,调用侧可读
function createCard({ title, price = 0, tags = [] } = {}) {
  if (!title) throw new TypeError('title 必填');
  return { title, price, tags, label: `${title} · ${price} 元` };
}

// 2 默认值只在 undefined 时生效
const withDefault = (n = 10) => n;
console.log(withDefault(undefined), withDefault(null)); // 10 null

// 3 剩余参数收集多余实参
const sum = (...nums) => nums.reduce((acc, n) => acc + n, 0);
console.log(sum(1, 2, 3));                              // 6

// 4 纯函数:同输入同输出,无副作用
const round = (n, digits = 2) => Number(n.toFixed(digits));
console.log(round(3.14159), round(3.14159));             // 3.14 3.14

// 5 有副作用时把副作用留在边界,核心逻辑保持纯函数
function applyDiscount(cart, rate) {
  const total = cart.reduce((acc, i) => acc + i.price * i.qty, 0);
  return { ...cart, total: round(total * (1 - rate)) };
}

const cart = [{ price: 19.9, qty: 2 }, { price: 5, qty: 1 }];
console.log(applyDiscount(cart, 0.1));                   // total: 40.32
console.log(cart);                                       // 原数组未被修改

// 6 立即执行函数:只在需要局部作用域时使用
const version = (() => {
  const tag = 'v1';
  return tag;
})();
console.log(version);                                    // v1

四种定义方式的差异见表 3-1。

方式 写法 this 绑定 适用场景
函数声明 function f() {} 调用时决定 需要提升、需要 this
函数表达式 const f = function () {} 调用时决定 条件性定义
箭头函数 const f = () => {} 继承外层 回调、工具函数
对象方法简写 { f() {} } 调用者 对象行为

参数设计规则见表 3-2。

规则 做法 原因
参数不超过三个 超出改用对象参数 调用侧无法记住顺序
可选参数给默认值 price = 0 避免函数体内散布判空
非法输入立即抛错 入口 throw new TypeError 错误在最近的位置暴露
不修改传入对象 返回新对象或显式命名 mutateXxx 调用方难以预期副作用

4 验证与讨论

  1. 执行脚本,确认输出 6、3.14 3.14、total: 40.32。
  2. 调用 createCard({}),确认抛出 title 必填,且错误信息指明参数名。
  3. 把 round 改为直接修改外部变量,观察同一输入两次输出不同,理解纯函数的判定标准。
  4. 用 console.log(cart) 确认 applyDiscount 未改变原数组。
检查项 预期结果 说明
默认值边界 传 null 得到 null 默认值只对 undefined 生效
剩余参数 sum(1,2,3) 为 6 以数组形式收集
纯函数 两次调用结果一致 无外部状态依赖
不可变返回 原 cart 未变化 展开生成新对象

讨论与局限。 箭头函数没有自己的 arguments 与 this,不能用作构造函数,也不能在需要动态 this 的对象方法中替换普通函数,详见 B02。另,默认值为对象字面量时每次调用都会重新创建,适合做兜底但不适合当缓存。

5 常见问题

现象 原因 处理
默认值不生效 传了 null 而非 undefined 调用侧用 ?? 兜底,或函数内判空
修改入参导致他处出错 函数内直接改对象属性 展开复制后再改
arguments 在箭头函数中报错 箭头函数无 arguments 改用剩余参数
返回值总是 undefined 函数体漏写 return 检查每个分支是否返回
回调里 this 不是预期对象 普通函数作为回调 改用箭头函数或 bind

6 结论

序号 结论 依据
1 参数超过三个改用对象参数 表 3-2
2 默认值只对 undefined 生效 3.2 节
3 核心逻辑写成纯函数,副作用留在边界 图 3-1
4 函数不修改入参,除非命名显式声明 表 3-2

参考文献

[1] MDN Web Docs · 函数,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions(访问于 2026-10-31)
[2] ECMA-262 · 10 Ordinary and Exotic Objects Behaviours,https://tc39.es/ecma262/#sec-ordinary-and-exotic-objects-behaviours(访问于 2026-10-31)

返回文章列表

评论 0

0/2000

还没有评论,快来抢沙发吧~