freeCodeCamp 基础 JavaScript 课程解析:用函数(Functions)编写可复用代码
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
在 freeCodeCamp 的 JavaScript 算法与数据结构课程的Basic JavaScript板块中,"Write Reusable JavaScript with Functions" 这一课首次系统性地引入函数概念,教你把代码拆分为可复用、可随时调用的小单元。通过本课学习,你不仅能掌握函数定义与调用的基础语法,还能理解 freeCodeCamp 在线课堂如何通过自动测试逐条校验你的代码,为后续学习传参、返回值与作用域奠定基础。
一、本节挑战在课程中的位置
本课对应的挑战源文件位于仓库curriculum/challenges/english/blocks/basic-javascript/56bbb991ad1ed5201cd392cf.md,文件名中嵌入的56bbb991ad1ed5201cd392cf是它在 freeCodeCamp 课程体系中的唯一挑战 ID。
从 Basic JavaScript 板块结构定义 可以看到,该板块(helpCategory 为 JavaScript,共 113 道挑战)的编排逻辑:
| 序号 | 挑战标题 | 主题 |
|---|---|---|
| 47 | Shopping List | 购物清单(数组综合练习) |
| 48 | Write Reusable JavaScript with Functions | 函数入门(本课) |
| 49 | Passing Values to Functions with Arguments | 参数与实参 |
| 50 | Return a Value from a Function with Return | return 返回值 |
| 51 | Global Scope and Functions | 全局作用域 |
| 52 | Local Scope and Functions | 局部作用域 |
| 53 | Global vs. Local Scope in Functions | 全局与局部作用域对比 |
也就是说,本课是 Basic JavaScript 板块中进入函数世界的起点:它先让学习者认识"函数可以把一段代码打包成可复用单元",随后的课程才逐步加入参数、返回值与作用域规则。理解了这一点,就能明确本课的目标边界——只聚焦于"定义 + 调用"两个最基础动作。
二、核心概念:什么是函数
原文档给出的定义非常精炼:
In JavaScript, we can divide up our code into reusable parts calledfunctions.
即:函数(function)是把一段代码划分出来的可复用部分。当同一段逻辑需要在多处出现时,不必复制粘贴整段代码,而是把它放进函数里,需要时调用即可。
函数声明(function declaration)的基本形态如下:
function functionName() { console.log("Hello World"); }逐段拆解这段语法:
function:声明函数的关键字;functionName:函数名,遵循标识符命名规则,应能表达函数用途;- 一对圆括号
():函数定义阶段的参数占位位置。本课示例不含参数,圆括号为空; - 一对花括号
{}:函数体,即每次调用函数时都会执行的全部语句。
原文档强调了一个重要事实:花括号之间的所有代码,每次调用函数时都会被完整执行一遍。因此函数天然具备"复用逻辑、统一行为"的价值——只要修改函数体一处,所有调用点都会同步生效。
三、调用函数:名称加圆括号
定义函数只是"打包代码",真正执行还需要调用(call)或调用(invoke)它。调用方式是用函数名加一对圆括号:
functionName();执行上述语句后,控制台(dev console)会输出一次Hello World。
这里有几个初学者容易混淆的点,值得强调:
- 定义与调用是两步动作。只写
function functionName() { ... }不会触发函数体执行;必须出现functionName();才会真正运行。 - 圆括号是"调用标记"。函数名后面紧跟
()表示要执行它;如果不加圆括号而只写functionName,在 JavaScript 中拿到的是对函数本身的引用(后续学习回调函数时常用),函数体并不会执行。 - 可以反复调用。
functionName(); functionName();会连续输出两次Hello World,这正是"可复用"最直观的体现。
四、本课任务:创建并调用 reusableFunction
--instructions--部分给出了两道明确指令:
- 创建一个名为
reusableFunction的函数,向控制台打印字符串Hi World; - 调用该函数。
挑战的初始代码(--seed-contents--)是一个空白的编辑器,需要你从零写起。--solutions--部分给出了官方参考答案:
function reusableFunction() { console.log("Hi World"); } reusableFunction();对照参考答案可以确认两个要点:
- 函数体的输出内容必须精确匹配
Hi World(大小写、空格都不能错),因为自动判题会做严格字符串比对; - 函数定义之后必须单独调用一次
reusableFunction();,两条指令缺一不可。
五、自动判题机制:hints 是如何验证你通过的
freeCodeCamp 的挑战都通过 Markdown 中的--hints--段落声明自动测试。本课共有三条断言,逐条对应前面的指令要求。
1. reusableFunction 必须是一个函数
assert(typeof reusableFunction === 'function');通过typeof运算符确认reusableFunction的类型是function。只要定义动作缺失或定义成了普通变量,这条就会失败。
2. 调用后必须输出 Hi World
assert(testConsole());这条依赖文档中--before-each--段落注入的辅助函数testConsole。它在每次测试执行前会:
- 缓存原始
console与原生console.log; - 临时覆写
console.log,当检测到消息精确等于Hi World时置hiWorldWasLogged = true; - 在覆写期间主动调用一次
reusableFunction(); - 最后恢复原生
console.log并返回hiWorldWasLogged。
因此这条断言验证的不只是"函数被定义",而是"调用后确实向控制台输出了正确字符串"。
3. 定义之后必须真的调用过
const functionStr = reusableFunction && __helpers.removeWhiteSpace(reusableFunction.toString()); const codeWithoutFunction = __helpers.removeWhiteSpace(__helpers.removeJSComments(code)).replace(/reusableFunction\(\)\{/g, ''); assert(/reusableFunction\(\)/.test(codeWithoutFunction));这条测试先剔除代码中的注释与空白字符(__helpers.removeWhiteSpace、__helpers.removeJSComments),再把"定义语句本身的reusableFunction(){}"模式移除,最后用正则/reusableFunction\(\)/检查源码中确实存在独立的调用表达式。这样的写法能有效避免"只定义、没调用"或"通过注释里的函数名蒙混过关"的取巧情况。
六、挑战源文件的结构化格式
本课文件(.md)本身并不是给人阅读的普通文章,而是 freeCodeCamp 课程引擎可以解析的结构化源数据。文件开头由---包裹的 frontmatter 声明了元信息:
id: 56bbb991ad1ed5201cd392cf # 挑战唯一 ID,课程顺序引用依赖它 title: Write Reusable JavaScript with Functions # 挑战标题 challengeType: 1 # 挑战类型标识 forumTopicId: 18378 # 关联论坛讨论主题 ID dashedName: write-reusable-javascript-with-functions # URL 友好的短横线命名正文则通过若干语义化区块组织内容,本课用到了:
| 区块 | 作用 |
|---|---|
# --description-- | 面向学习者的概念讲解(本课的函数介绍与示例) |
# --instructions-- | 可操作的任务清单 |
# --before-each-- | 每次测试运行前注入的辅助脚本 |
# --hints-- | 自动判定的断言清单 |
# --seed-contents-- | 初始种子代码(本课为空,需从零编写) |
# --solutions-- | 官方参考答案,用于示例展示与测试对照 |
如果对这套格式本身感兴趣,可以进一步查看仓库中的挑战结构与校验相关工具,例如curriculum/schema/challenge-schema.js(挑战结构约束)与 Basic JavaScript 板块结构定义 basic-javascript.json。而本课源文件同目录下还存放着整个 Basic JavaScript 板块的全部 100 余个挑战,例如前一课的 Shopping List 与紧随其后的 Passing Values to Functions with Arguments。
七、学完本课后:下一步与自学建议
在课程中的衔接
本课只是函数的"第一课"。紧接着的下一个挑战 Passing Values to Functions with Arguments 就会引入**参数(parameters)与实参(arguments)**的概念:参数是函数定义时占位用的变量,实参是调用时真正传入的值。例如:
function testFun(param1, param2) { console.log(param1, param2); } testFun("Hello", "World");再往后,课程还会依次讲解return返回值、全局/局部作用域,以及"undefined 返回值"等进阶主题。因此建议按板块顺序逐课推进,确保函数体系的知识环环相扣。
自学验证建议
在本地浏览器控制台或 Node.js 环境中,可以自行验证本课知识:
function reusableFunction() { console.log("Hi World"); } reusableFunction(); // 控制台输出: Hi World reusableFunction(); // 再次调用,再次输出: Hi World尝试对比以下两种情况,能帮助你加深对"定义"与"调用"区别的理解:
// 只定义、不调用:没有任何控制台输出 function demo() { console.log("never shown"); } // 引用而不调用:同样没有任何输出,demo 是函数对象本身 demo; // 调用:圆括号触发执行 demo();结语
"Write Reusable JavaScript with Functions" 虽然只覆盖了函数最基础的"定义 + 调用"语法,但它奠定了整个函数体系的学习起点。通过本文对挑战源文件、板块排序与自动判题逻辑的逐层拆解,你可以看到 freeCodeCamp 如何用结构化的 Markdown 源数据驱动一套完整的在线练习闭环——而理解这套机制,无论对你完成当前挑战,还是日后为开源社区贡献课程内容,都大有裨益。
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考