引言

在前面两篇文章中,我们学习了函数的基本概念,并亲手创建了自定义函数。但到目前为止,我们创建的函数主要是在执行一些操作,比如在页面上显示消息框,而并没有将任何处理结果反馈给调用者。在实际编程中,很多函数的作用不仅仅是执行某个动作,更重要的是完成某种计算并将结果传递回来,供后续代码使用。这种函数执行完毕后回传的结果就是返回值


一、什么是返回值

返回值这个词非常直白,它指的就是函数执行完毕后返回的那个值。虽然你可能没有刻意思考过这个概念,但在之前的学习中你已经无数次接触过返回值了。每当你在代码中调用一个函数,并将它的结果赋值给一个变量,或者直接使用它的结果进行其他操作时,你就在使用返回值。

1.1 熟悉的例子

const myText = "天气很冷";
const newString = myText.replace("冷", "热");
console.log(newString); // 输出 "天气很热"

在这段代码中,myText.replace("冷", "热") 这个函数调用执行后,返回了一个新字符串 "天气很热"。这个返回值被赋值给了 newString 变量。如果���们不把返回值存起来,这个新字符串就会丢失,因为它只是临时产生的一个值。

在MDN文档中查阅任何一个函数的说明页面,你都会找到一个名为"返回值"的专门章节。文档编写者深知理解返回值对于正确使用函数至关重要,养成查阅文档中返回值说明的习惯,能让你少走很多弯路。


二、有返回值与无返回值的函数

并不是所有函数都有有意义的返回值。在我们的参考文档中,有些函数的返回值被标记为空值 void 或未定义值 undefined,这意味着这个函数执行完毕后不会回传任何有用的数据。

// 无返回值的函数:只执行操作(副作用),不返回数据
function displayMessage(msgText, msgType) {
  const html = document.querySelector("html");
  const panel = document.createElement("div");
  panel.setAttribute("class", "msgBox");
  html.appendChild(panel);

  const msg = document.createElement("p");
  msg.textContent = msgText;
  panel.appendChild(msg);

  const closeBtn = document.createElement("button");
  closeBtn.textContent = "x";
  panel.appendChild(closeBtn);

  closeBtn.onclick = function () {
    panel.parentNode.removeChild(panel);
  };
  // 没有 return 语句 → 默认返回 undefined
}

// 有返回值的函数:计算并返回结果
function squared(num) {
  return num * num;
}
类型 特征 示例
无返回值函数 执行副作用操作,没有 return 语句或返回 undefined displayMessage()
有返回值函数 通过 return 回传计算结果 squared(num)

有返回值的函数通常被用作计算的中间步骤。一个函数处理完数据后返回结果,这个结果可以作为另一个函数的输入,形成处理管道。这种链式处理数据的方式是函数式编程思想的体现。


三、使用return关键字

要从自定义函数中返回值,你需要使用 return 关键字。return 语句有两个作用:

作用 说明
指定返回值 return 后面的表达式的值就是函数的返回值
终止函数执行 一旦执行到 return,函数立即停止运行,之后的代码不会被执行

3.1 基本示例

我们之前见过的一个实际例子来自 random-canvas-circles.html

function randomNumber(number) {
  return Math.floor(Math.random() * number);
}

这个函数接收一个整数参数 number,函数体只有一行 return 语句:

  1. Math.random() 生成一个 [0, 1) 的随机小数
  2. 乘以 number 后得到 [0, number) 的随机小数
  3. Math.floor() 向下取整,得到 [0, number-1] 的随机整数
  4. 最终结果通过 return 返回给调用者

3.2 两种写法对比

// 写法一:直接在 return 中计算(更简洁)
function randomNumber(number) {
  return Math.floor(Math.random() * number);
}

// 写法二:先计算再返回(更易读,适合复杂逻辑)
function randomNumber(number) {
  const result = Math.floor(Math.random() * number);
  return result;
}

两种写法功能完全相同。当计算逻辑比较复杂时,先计算再返回可能让代码更容易理解。

3.3 "函数调用被返回值替换"的思维模型

理解这种思维模型非常重要。比如在 draw 函数中调用了三次 random

ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);

假设这三次 random 调用分别返回了 50020035,那么这行代码实际执行时就等价于:

ctx.arc(500, 200, 35, 0, 2 * Math.PI);

执行流程

random(WIDTH)   → 计算 → 返回 500  → 替换调用位置
random(HEIGHT)  → 计算 → 返回 200  → 替换调用位置
random(50)      → 计算 → 返回 35   → 替换调用位置
→ ctx.arc(500, 200, 35, 0, 2 * Math.PI)  → 执行

JavaScript引擎会先执行函数调用,获取返回值,然后用返回值替换掉函数调用的位置,最后再执行外层语句


四、主动实践:构建带返回值的函数库

理论知识讲再多也不如亲手写一遍。让我们构建一个简单的函数库,包含几个数学计算函数,每个函数都接收参数并返回计算结果。

4.1 三个计算函数

function squared(num) {
  return num * num;
}

function cubed(num) {
  return num * num * num;
}

function factorial(num) {
  var x = num;
  while (x > 1) {
    num *= x - 1;
    x--;
  }
  return num;
}
函数 功能 示例调用 返回值
squared(5) 计算平方 5 * 5 25
cubed(5) 计算立方 5 * 5 * 5 125
factorial(5) 计算阶乘 5 × 4 × 3 × 2 × 1 120

factorial 函数的实现使用了 while 循环来累乘:

// 以 factorial(5) 为例,执行流程:
// 初始:num=5, x=5
// 第1次循环:num=5×(5-1)=20, x=4
// 第2次循环:num=20×(4-1)=60, x=3
// 第3次循环:num=60×(3-1)=120, x=2
// 第4次循环:num=120×(2-1)=120, x=1
// x>1 为 false,循环终止
// return 120

4.2 事件监听与输入处理

为了让这些函数真正工作起来,需要监听输入框的变化事件:

input.addEventListener("change", () => {
  const num = parseFloat(input.value);
  if (isNaN(num)) {
    para.textContent = "You need to enter a number!";
  } else {
    para.textContent = `${num} squared is ${squared(num)}. `;
    para.textContent += `${num} cubed is ${cubed(num)}. `;
    para.textContent += `${num} factorial is ${factorial(num)}. `;
  }
});

事件处理流程

用户输入 → "change"事件触发
  ↓
parseFloat(input.value)  将字符串转为浮点数
  ↓
isNaN(num)?
  ├─ true  → 显示错误提示 "You need to enter a number!"
  └─ false → 依次调用三个函数,用返回值拼接输出字符串
关键函数 作用
parseFloat() 将字符串解析为浮点数,无法解析时返回 NaN
isNaN() 检测一个值是否为 NaN,是则返回 true

这个输入验证很重要——如果用户在输入框中输入了字母或其他非数字内容,parseFloat 会返回 NaN,而用 NaN 进行数学运算会导致所有结果都变成 NaN,产生无意义的输出。

这里可以看到返回值的实际运用:每个函数调用都返回一个数值,这些数值直接在字符串模板中被拼接显示。如果没有返回值机制,我们需要在每个函数内部直接操作DOM来显示结果,代码会变得混乱且难以复用。


五、函数库的思维与错误处理

5.1 函数库的概念

当你能熟练编写自己的函数后,自然会产生构建个人函数库的想法。函数库就是一组相关函数的集合,可以保存在一个单独的JavaScript文件中,在需要的时候引入使用。

math-utils.js
├── squared(num)      // 计算平方
├── cubed(num)        // 计算立方
├── factorial(num)    // 计算阶乘
├── isPrime(num)      // 判断质数
└── gcd(a, b)         // 计算最大公约数

5.2 防御性编程

在编写函数时,养成良好的错误处理习惯非常重要:

// 带防御性检查的平方函数
function squared(num) {
  if (num === undefined || num === null) {
    console.warn("squared() 缺少参数");
    return 0; // 返回合理的默认值
  }
  return num * num;
}
原则 说明
验证必要参数 缺少关键参数时给出提示或返回默认值
提供参数默认值 可选参数使用 param = defaultValue
清晰的函数命名 squared 准确传达"计算平方"的含义

六、总结

函数返回值是JavaScript函数体系中最后一个基础概念,但它绝不是最不重要的。有了返回值,函数就不再只是执行操作的孤立代码块,而是变成了可以串联组合的数据处理单元。

核心要点回顾

知识点 核心内容
返回值定义 函数执行完毕后回传给调用者的值
return 关键字 指定返回值 + 终止函数执行
无返回值函数 执行副作用操作,默认返回 undefined
替换思维模型 函数调用被返回值替换,再执行外层语句
链式处理 一个函数的返回值作为另一个函数的输入
错误处理 验证参数、提供默认值、防御性编程

完整代码回顾

// ====== 函数定义 ======
function squared(num) {
  return num * num;
}

function cubed(num) {
  return num * num * num;
}

function factorial(num) {
  var x = num;
  while (x > 1) {
    num *= x - 1;
    x--;
  }
  return num;
}

// ====== 事件绑定 ======
input.addEventListener("change", () => {
  const num = parseFloat(input.value);
  if (isNaN(num)) {
    para.textContent = "You need to enter a number!";
  } else {
    para.textContent = `${num} squared is ${squared(num)}. `;
    para.textContent += `${num} cubed is ${cubed(num)}. `;
    para.textContent += `${num} factorial is ${factorial(num)}. `;
  }
});

如果本文中有任何概念让你感到困惑,不要犹豫,重新阅读相关章节或者动手在浏览器控制台中试验代码。编程学习最好的方式就是亲手实践。在后续的学习中,你还会接触到更高级的函数概念,比如将函数作为参数传递给另一个函数的回调函数模式,以及能够返回函数的函数。这些进阶内容都建立在扎实的基础概念之上,现在打好基础,未来的学习就会水到渠成。

更多推荐