首页
/ JavaScript 函数参数与实参传值机制详解:以 freeCodeCamp Basic JavaScript 挑战「Passing Values to Functions with Arguments」为例

JavaScript 函数参数与实参传值机制详解:以 freeCodeCamp Basic JavaScript 挑战「Passing Values to Functions with Arguments」为例

2026-09-07 14:34:16作者:庞眉杨Will

本篇文章聚焦 freeCodeCamp 开源课程 Basic JavaScript 单元中的核心关卡——通过函数实参(Arguments)为函数形参(Parameters)传值。文章以仓库中该关卡的源文件 56533eb9ac21ba0edf2244bd.md 为骨架,逐条拆解关卡描述、练习题、自动化测试(hints)与参考答案的实现逻辑,并结合同单元相邻关卡与课程结构配置,讲清"参数占位、实参传值"这一函数复用的基础机制。读完本文,你将能够:准确区分参数与实参、定义并调用带参数的函数、读懂 freeCodeCamp 关卡中基于 console.log 拦截的自动判题原理,并把这套技能迁移到真实 JavaScript 开发中。

关卡定位:它处于函数学习链条的哪个节点

在 freeCodeCamp 课程体系中,本关卡属于 Basic JavaScript 大单元(superblock)中的 basic-javascript 知识块(block)。知识块内每一道课程卡的顺序由 curriculum/structure/blocks/basic-javascript.json 中的 challengeOrder 数组定义,本关卡位于函数主题的承上启下位置:

  • 前置关卡 Write Reusable JavaScript with Functions:学习用无参函数封装可复用代码,例如 function functionName() { console.log("Hello World"); },通过 functionName(); 反复调用;
  • 本关卡 Passing Values to Functions with Arguments:把函数从"每次输出相同内容"升级为"接收外部传入的值",从而让同一函数处理不同数据;
  • 后继关卡 Return a Value from a Function with Return(源码见 56533eb9ac21ba0edf2244c2.md):学习函数如何把结果返回给调用方。

可见,本关卡解决的是"数据如何进函数"的问题,与上一关(函数内部输出)和下一关(函数外部取值)共同构成完整的数据流动闭环。从该关卡文件的 frontmatter 也能确认其属性:id: 56533eb9ac21ba0edf2244bdchallengeType: 1(普通编程练习)、dashedName: passing-values-to-functions-with-arguments

核心概念:参数(Parameters)与实参(Arguments)到底差在哪

关卡描述(--description-- 段落)给出了本主题最核心的定义:

参数(Parameters)是充当占位符(placeholders)的变量,用来接收将来函数被调用时输入的值。定义函数时,通常会同时定义一个或多个参数。 函数被调用时真正输入("传入")进去的实际值,被称为实参(Arguments)

一个容易混淆的点在于中文语境常把两者都叫"参数"。二者的本质区别可以概括为:

概念 英文 出现时机 作用 类比
参数(形参) Parameter 定义函数时写在括号里 声明"这个函数需要几个什么样的输入" 表格里预先留好的空栏位
实参 Argument 调用函数时写在括号里 把真实数据填入占位位置 调用时填进栏位的真实内容

在同一函数被多次调用时,形参保持不变(它们只是变量声明),而实参每次都可以不同——这正是函数能够复用与参数化的根源。正如关卡原文强调的:"you could call testFun again with different arguments and the parameters would take on the value of the new arguments."(你可以用不同的实参再次调用 testFun,此时参数会取到新实参的值。)

定义带参数的函数:从 testFun 学语法

关卡中给出了一个带两个参数的函数示例:

function testFun(param1, param2) {
  console.log(param1, param2);
}

语法要点拆解:

  1. 函数名后的小括号内列出参数声明,多个参数用英文逗号分隔,如 (param1, param2)
  2. 参数本质上是函数内部的局部变量,无需(也不能)在括号里写 var / let / const
  3. 函数体内可以直接引用参数名参与逻辑,这里的 console.log(param1, param2) 会把两个参数值依次打印到开发控制台(dev console);
  4. 有参函数的函数体可以在声明时保持"内容未知",直到调用时参数被赋予具体值后才产生真实输出。

传值调用:实参如何"落到"形参上

示例的调用方式如下:

testFun("Hello", "World");

这里传入了两个字符串实参 HelloWorld。调用发生时,JavaScript 引擎会按位置一一对应完成绑定:

  • 第 1 个实参 "Hello" → 第 1 个形参 param1
  • 第 2 个实参 "World" → 第 2 个形参 param2

因此在函数体内,param1 的值等于字符串 "Hello"param2 等于 "World",控制台输出 Hello World。若再次调用:

testFun("Alice", "Bob");   // 输出 Alice Bob
testFun(42, 7);            // 输出 42 7(实参类型不必与上次一致)

param1param2 会随每次调用接收新的实参值。从语言机制上说,实参对应的是"按位置传值"——这种位置匹配(positional binding)是函数设计里最基础的约定,实参的数量与顺序直接决定了形参被赋予的值。

补充一点与本关卡密切相关的语言事实:在 JavaScript 中,像字符串、数字这类基本类型实参是按值传递的——函数内部对参数重新赋值不会影响函数外部的原变量;而关卡练习使用的正是数字与字符串,因此只需关注"值流入函数"这一单向过程即可。

实战练习拆解:实现 functionWithArgs 并求和

关卡 --instructions-- 给出两道明确的任务:

  1. 创建一个名为 functionWithArgs 的函数,它接收两个实参,并把它们的输出到开发控制台;
  2. 定义完之后,用两个数字作为实参调用该函数。

参考上一关"把结果用 console.log 打印出来"的写法(而非 return 返回),本关的标准实现是:

function functionWithArgs(a, b) {
  console.log(a + b);
}

functionWithArgs(10, 5);

逐句分析这份参考解:

  • function functionWithArgs(a, b):声明两个形参 ab 作为加法运算的操作数占位;
  • console.log(a + b):用 + 运算符对两个形参求和并打印。注意这里输出的是"计算结果",而不是像上一关那样输出固定的问候语;
  • functionWithArgs(10, 5):函数定义后再单独一行调用,传入数字 105,触发 console.log 输出 15

理解要点:函数体里只写了"如何做"(把两个数加起来),具体"做什么数"由调用处的实参决定。此后只需更换实参即可复用同一函数:

functionWithArgs(1, 2);  // 控制台输出 3
functionWithArgs(7, 9);  // 控制台输出 16
functionWithArgs(2, 3);  // 控制台输出 5

这也是该函数被称为 reusable(可复用)的原因:一段求和逻辑被抽象成可参数化的单元。

读懂自动判题(hints):关卡是如何验证你的答案的

freeCodeCamp 每个关卡都会附带一组自动化测试断言,存储在源文件的 --hints-- 段落中。本关卡的 4 条断言精准刻画了"正确解法"必须满足的行为,也反过来说明了对本主题的考核点。关卡开头还有一个 --before-each-- 段落,声明了测试前置于全局的变量:

var logOutput;

它专门用于承接被拦截的 console.log 输出。

断言一:functionWithArgs 必须是函数

assert(typeof functionWithArgs === 'function');

如果学习者没有定义函数,或定义成了变量/字符串,typeof 将不等于 'function',测试即失败。它校验的是"必须声明函数"这一语法事实。

断言二与三:特定实参组合必须产生特定输出

console.log = function (message) {
  logOutput = message
};
functionWithArgs(1, 2);
assert(logOutput == 3);
console.log = function (message) {
  logOutput = message
};
functionWithArgs(7, 9);
assert(logOutput == 16);

这两条揭示了判题的关键机制:测试临时替换了全局 console.log,把原本打印到控制台的信息改存到 logOutput 变量,再调用你的 functionWithArgs(1, 2) 并断言 logOutput == 3。这意味着:

  • 你的函数必须通过 console.log(...) 输出结果,否则测试无法截获任何内容;
  • 输出必须是一个数字 3 而非字符串 "3",因此 assert(logOutput == 3) 使用宽松相等 ==(同时兼容字符串 "3",但正确做法仍是直接打印数字和);
  • 函数内部必须是"求和"逻辑,console.log(a + b) 而非 console.log(a, b)(后者会被断言为 12 而非 3)。

这也解释了为什么练习要求"输出到 dev console"而不是使用 return:本阶段考核点是参数接收与输出,返回值能力留给了下一关卡专门讲授。

断言四:定义之后必须用两个数字调用函数

assert(
  /functionWithArgs\([-+]?\d*\.?\d*,[-+]?\d*\.?\d*\)/.test(
    __helpers.removeJSComments(code).replace(/\s/g, '')
  )
);

这条断言使用了正则表达式匹配整段学习者代码code),并先调用 removeJSComments 剔除注释、再用 replace(/\s/g, '') 移除所有空白后检测。正则的语义是:

  • functionWithArgs\(:匹配函数调用写法;
  • [-+]?\d*\.?\d*:允许可选的 +/- 符号、任意位数字以及可选小数点(覆盖整数与小数);
  • ,:两个实参之间用逗号分隔;
  • 整体要求调用恰好包含两个数字实参。

换言之,即使代码逻辑正确,如果你定义函数后没有调用,或调用时传的不是两个数字,本断言也会失败。

逐条验证:把答案在浏览器控制台跑一遍

你可以脱离判题环境,直接在任意浏览器开发者工具(F12 → Console)中逐步验证判题逻辑:

// 第 1 步:粘贴你的答案
function functionWithArgs(a, b) {
  console.log(a + b);
}
functionWithArgs(10, 5);   // 手动验证:控制台应出现 15

// 第 2 步:模拟判题环境(拦截 console.log)
var logOutput;
console.log = function (message) {
  logOutput = message;
};

// 第 3 步:模拟断言二与三
functionWithArgs(1, 2);
logOutput == 3;   // 应返回 true
functionWithArgs(7, 9);
logOutput == 16;  // 应返回 true

如果第 3 步返回 false,请重点检查:形参是否用了 + 求和;console.log 是否打印的是和而不是两个数;调用实参是否是两个数字(而非字符串,字符串 "1" + "2" 会拼接成 "12")。

从本关卡到真实开发:参数化让代码获得复用能力

理解传参的意义远超通过一道关卡。把"固定逻辑"升级为"参数化函数",是代码复用与抽象的第一步,也是后续课程(作用域、返回值、箭头函数、函数作为参数等)的共同基石:

  1. 行为可定制:同一函数配合不同实参即可应对不同输入,避免复制粘贴整段逻辑;
  2. 心智负担更低:调用方只需关心"传入什么"与"得到什么",无需理解函数内部细节;
  3. 可测试性更强:正如本关卡的 console.log 拦截技巧所展示的,参数化的纯逻辑容易被独立验证——这是单元测试能够存在的前提。

从课程结构配置看,curriculum/structure/blocks/basic-javascript.json 将本关卡安排在前置的 Write Reusable JavaScript with Functions 之后、Return a Value from a Function with ReturnGlobal Scope and Functions 之前,恰好遵循"先能复用函数 → 再能传值进入 → 再能取回结果 → 再谈作用域边界"的渐进路径。阅读源码时,你可以在关卡文件目录中按顺序对照 56533eb9ac21ba0edf2244bd.md(本关)与前后相邻 id 的 .md 文件,逐关体会知识链条如何一步步展开。

掌握"实参按位置传给形参"这一条规则,你就拿到了理解 JavaScript 函数体系的钥匙——此后遇到默认参数、剩余参数(...args)、回调函数乃至高阶函数,都能回到这同一个模型上去理解。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389