首页
/ Web-Dev-For-Beginners「Fun with Functions」练习全解:JavaScript 函数定义、参数、默认值与返回值实战

Web-Dev-For-Beginners「Fun with Functions」练习全解:JavaScript 函数定义、参数、默认值与返回值实战

2026-09-07 16:31:26作者:谭伦延

本篇指南基于 Web-Dev-For-Beginners(Web Dev 101,"24 Lessons, 12 Weeks" 在线课程仓库)中 JavaScript 基础模块的第二课作业《Fun with Functions》(assignment.md,仓库内同时提供 孟加拉语译本)展开。作业要求你在 functions-practice.js 中实现 6 个层层递进的函数,覆盖无参函数、带参函数、默认参数、返回语句以及综合应用,并配套完整的测试调用与评分标准。读完并动手完成本篇后,你将能够独立写出带默认参数和返回值规范的 JavaScript 函数,理解"只打印"与"返回结果"的本质区别,并通过加分挑战接触箭头函数与高阶函数等现代语法。

JavaScript 函数课程手绘速记图(Web-Dev-101 Functions Sketchnote)

作业定位:它在课程中的位置

这份作业隶属于课程第 2 个模块 JavaScript Basics(2-js-basics) 下的第 2 课 Functions and Methods。该模块共 4 课:变量与数据类型、函数与方法、条件判断、数组与循环。本课是其中承上启下的一课——前一课 Variables and Data Types 教你"存数据",本课教你"封装逻辑",而后面的 Arrays and Loops 将大量依赖你本课写好的函数来处理批量数据。

作业文档的官方说明是:通过创建不同类型的函数,强化你对 JavaScript 函数、参数(parameters)、默认值(default values)和返回语句(return statements)的理解。下面按作业原文的五个部分逐一拆解,每部分都给出可直接运行的参考实现与原理说明。

准备:文件与运行环境

作业要求创建一个名为 functions-practice.js 的 JavaScript 文件并实现下列全部函数。该文件由你自己创建(仓库中没有该文件,这是刻意留给你动手的练习位),放在任意位置都可以,典型做法是:

  1. 在浏览器中新建一个空白 HTML 页面,通过 <script src="functions-practice.js"></script> 引入该文件;
  2. F12 打开浏览器的开发者工具(Developer Console)——这是作业文档末尾特别提示的技巧:只有打开控制台,才能看到 console.log() 的输出。

下面所有代码块均可直接复制到 functions-practice.js 中运行验证。

第 1 部分:基础函数(Basic Functions)

1. sayHello:无参函数

要求:创建一个叫 sayHello 的函数,不接受任何参数,向控制台输出 "Hello!"

参考实现:

// 无参函数:圆括号内为空
function sayHello() {
  console.log('Hello!');
}

从源码结构看,这正是配套课程讲义(2-js-basics/2-functions-methods/README.md)中 displayGreeting 例子的同构形态:function 关键字 + 函数名 + 圆括号 + 花括号函数体。注意两点:

  • 调用时必须写圆括号 sayHello()——只写 sayHello 而不加括号,引用的是函数本身而非执行它;
  • console.log() 严格来说是一个方法(method)——挂在 console 对象上的函数。课程讲义专门提示了 function 与 method 的区别:方法依附于对象,函数独立存在;日常口语中开发者常混用这两个词。

2. introduceYourself:带参函数

要求:创建 introduceYourself 函数,接受一个 name 参数,向控制台输出形如 "Hi, my name is [name]" 的消息。

参考实现:

// 带参函数:name 是占位符(参数),调用时传入的值称为实参(argument)
function introduceYourself(name) {
  console.log(`Hi, my name is ${name}`);
}

这里有两个知识点:

  • 参数 vs 实参:定义时的 name 是参数(占位符),调用 introduceYourself('Sarah') 时传入的 'Sarah' 是实参。JavaScript 会把实参按位置绑定到对应参数上;
  • 模板字符串(template literal):用反引号 ` 包裹字符串,用 ${name} 把变量直接插进文本里。这是课程讲义推荐拼接动态消息的惯用写法,比 'Hi, my name is ' + name 更清晰。

第 2 部分:带默认参数的函数(Default Parameters)

3. greetPerson:必填参数 + 可选参数

要求:创建 greetPerson 函数,接受两个参数:name(必填)和 greeting(可选,默认值为 "Hello")。函数向控制台输出形如 "[greeting], [name]!" 的消息。

参考实现:

// greeting 的默认值是 'Hello':调用时不传第二个参数时自动使用
function greetPerson(name, greeting = 'Hello') {
  console.log(`${greeting}, ${name}!`);
}

调用效果:

greetPerson('Alex');        // 输出: Hello, Alex!
greetPerson('Maria', 'Hi'); // 输出: Hi, Maria!

默认参数的工作机制(可以推断,符合 ECMAScript 标准行为):当调用方没有为某个参数提供值(即该位置实参为 undefined)时,引擎使用函数定义中 = 后面的默认表达式。这带来两个工程上的好处:

  1. 向后兼容:旧调用 greetPerson('Alex') 在新参数加入后无需修改;
  2. 降低使用门槛:调用者先获得一个合理的默认行为,需要时再自定义——课程讲义称之为让函数"更友好"的实用技巧。

注意默认参数必须写在参数列表的靠后位置:如果把 greeting = 'Hello' 放在 name 前面,调用 greetPerson('Alex')'Alex' 会先绑定给 greeting,语义就错了。本作业刻意把必填参数放前面、可选参数放后面,就是这个原因。

第 3 部分:返回值的函数(Return Values)

4. addNumbers:返回两数之和

要求:创建 addNumbers 函数,接受 num1num2 两个参数,返回它们的和。

参考实现:

function addNumbers(num1, num2) {
  return num1 + num2;   // return 把结果交还给调用方,并立即结束函数
}

5. createFullName:返回拼接后的字符串

要求:创建 createFullName 函数,接受 firstNamelastName 两个参数,把完整姓名作为单个字符串返回。

参考实现:

function createFullName(firstName, lastName) {
  return `${firstName} ${lastName}`;
}

return 语句是这部分的核心考点,课程讲义用流程图强调了两条语义:

  • 执行到 return 时,函数立即停止运行并把值交还给调用方,return 之后的代码不会再执行;
  • 没有 return 的函数只会执行动作(比如打印日志),最终隐式返回 undefined

这也正是作业评分标准中单独列出"返回值"一行的原因:该返回的函数必须正确返回值,不该返回的函数(如 sayHellogreetPerson)只做动作即可。返回值让"调用方决定如何使用结果"成为可能——存入变量、参与表达式、再传给别的函数——这就是讲义所说"返回值的函数更通用、代码更模块化"的含义。

第 4 部分:概念综合(Mix It All Together)

6. calculateTip:默认参数 + 返回值 + 数值计算

要求:创建 calculateTip 函数,接受两个参数:billAmount(必填)和 tipPercentage(可选,默认值 15)。函数计算并返回小费金额。

参考实现:

// 默认小费比例 15%:calculateTip(50) 等价于 calculateTip(50, 15)
function calculateTip(billAmount, tipPercentage = 15) {
  return (billAmount * tipPercentage) / 100;
}

这个函数把前三部分的能力全部揉在一起:必填/可选参数、默认值、返回数值。注意 tipPercentage 是"百分比"而非"小数",所以要除以 100;calculateTip(50) 应返回 7.5

第 5 部分:测试你的函数(Test Your Functions)

作业原文给出的测试调用示例(完整继承,可直接使用):

// Test your functions here
sayHello();
introduceYourself("Sarah");
greetPerson("Alex");
greetPerson("Maria", "Hi");

const sum = addNumbers(5, 3);
console.log(`The sum is: ${sum}`);

const fullName = createFullName("John", "Doe");
console.log(`Full name: ${fullName}`);

const tip = calculateTip(50);
console.log(`Tip for $50 bill: $${tip}`);

在浏览器控制台(F12)中运行上述全部代码,预期输出依次为:

Hello!
Hi, my name is Sarah
Hello, Alex!
Hi, Maria!
The sum is: 8
Full name: John Doe
Tip for $50 bill: $7.5

如果实际输出与预期不一致,最常见的两类错误是:把"应该 return 的函数"写成了 console.log(控制台有输出但 sum 变成 undefined);或忘记写默认值导致 greetPerson('Alex') 打印出 undefined, Alex!。对照评分标准第 3 行逐项自查即可。

完整参考答案:functions-practice.js

把六部分合并,一份完整可运行的 functions-practice.js 如下(变量名、命名均遵循作业要求的 camelCasing 规范):

// ============ Part 1: Basic Functions ============
function sayHello() {
  console.log('Hello!');
}

function introduceYourself(name) {
  console.log(`Hi, my name is ${name}`);
}

// ============ Part 2: Functions with Default Parameters ============
function greetPerson(name, greeting = 'Hello') {
  console.log(`${greeting}, ${name}!`);
}

// ============ Part 3: Functions that Return Values ============
function addNumbers(num1, num2) {
  return num1 + num2;
}

function createFullName(firstName, lastName) {
  return `${firstName} ${lastName}`;
}

// ============ Part 4: Mix It All Together ============
function calculateTip(billAmount, tipPercentage = 15) {
  return (billAmount * tipPercentage) / 100;
}

// ============ Part 5: Test Your Functions ============
sayHello();
introduceYourself('Sarah');
greetPerson('Alex');
greetPerson('Maria', 'Hi');

const sum = addNumbers(5, 3);
console.log(`The sum is: ${sum}`);

const fullName = createFullName('John', 'Doe');
console.log(`Full name: ${fullName}`);

const tip = calculateTip(50);
console.log(`Tip for $50 bill: $${tip}`);

评分标准(Rubric)

作业附带的评分表(对照原文 assignment.md 翻译如下),提交前可逐行自查:

标准 优秀(Exemplary) 合格(Adequate) 待改进(Needs Improvement)
函数创建 6 个函数全部正确实现,语法与命名规范均正确 4–5 个函数正确实现,存在轻微语法问题 仅实现 3 个及以下,或存在重大语法错误
参数与默认值 必填参数、可选参数与默认值均按规范正确使用 参数使用正确,但默认值处理可能有问题 参数实现错误或缺失
返回值 应返回值的函数正确返回;不应返回的函数仅执行动作 大部分返回值正确,存在轻微问题 return 语句存在明显问题
代码质量 代码清晰、组织有序,变量命名有意义,缩进正确 代码可运行,但可读性或组织可更好 代码难以阅读或结构混乱
测试 所有函数均被恰当测试,结果展示清晰 大部分函数得到恰当测试 函数测试不足或测试错误

其中"代码质量"一行的命名规范,对应课程讲义中的最佳实践清单:给函数取清晰描述性的名字、多词名用 camelCasing(如 calculateTotal 而非 calculate_total)、每个函数聚焦做好一件事。

核心原理深化:结合课程讲义理解作业考点

作业本身只有需求与评分标准,配套讲义(2-js-basics/2-functions-methods/README.md)给出了每个考点背后的原理,以下要点与作业直接对应。

为什么同一个函数可以反复调用

函数定义一次、调用任意次。讲义的类比是亨利·福特的流水线标准件:逻辑一旦封装成可靠"部件",在任何需要的地方直接使用而无需重建。反过来想维护成本——同一段逻辑散落在 20 处时,修改一处就要改 20 处,而封装成函数后只改一处。这是作业把 6 个函数都设计成"一次定义、多次测试调用"的动机。

调用与定义的顺序无关

JavaScript 引擎会处理函数声明的提升(hoisting):你可以在定义之前调用 sayHello(),运行依然正常。这一点与 const 声明的箭头函数不同,属于函数声明特有的行为。

console.log 是方法,不是函数

整个作业中你反复使用的 console.log() 属于挂在 console 对象上的方法。讲义给出的判别标准:方法依附于对象(写作 对象.方法()),函数独立声明存在。作业里的 sayHello()greetPerson() 都是后者。

默认参数为何"更灵活"

讲义用状态图对比了三种形态:无参函数输出恒定、带参函数随输入变化、带默认参数函数"可选定制且向后兼容"。对应到本作业,greetPersoncalculateTip 同时演示了同一函数被两种调用方式(greetPerson('Alex')greetPerson('Maria', 'Hi'))复用的灵活性——这正是默认参数的价值所在。

返回值为何优于直接打印

讲义的关键结论:返回值的函数更通用,因为调用方决定如何处理结果——显示到网页、拼进邮件、传给下一个函数都可以;而直接 console.log 把输出渠道写死在函数内部。这也是评分表把 addNumberscreateFullNamecalculateTip 必须返回、而 sayHello 等只做动作作为独立评分项的原因。

加分挑战(Bonus Challenges)

作业原文列出了三个可选进阶挑战,下面逐一给出实现思路与示例代码。

挑战 1:把其中一个函数改写成箭头函数

讲义指出箭头函数(arrow function)用 => 省去 function 关键字,是现代表达式的常见选择。最短改写:

// 无参 + 简写体:圆括号可省略参数名,单表达式自动返回
const sayHello = () => console.log('Hello!');

// 带参数 + 简写返回
const addNumbers = (num1, num2) => num1 + num2;

// 需要多语句时保留花括号,并显式 return
const calculateTip = (billAmount, tipPercentage = 15) => {
  const tip = (billAmount * tipPercentage) / 100;
  return tip;
};

讲义给出的选型建议:函数要复用就单独命名定义;一次性使用的回调考虑匿名/箭头形式。箭头函数通常绑定在 const 上,不具有函数声明那样的提升行为。

挑战 2:创建一个把另一个函数作为参数接受的函数

讲义用 setTimeout 示范了"函数作为参数"的模式——注意传的是函数本身(不带圆括号),把"何时执行"的决定权交给对方:

function displayDone() {
  console.log('3 seconds has elapsed');
}
// timer 单位是毫秒
setTimeout(displayDone, 3000);

// 等价写法一:匿名函数(只为这一次调用服务,不必命名)
setTimeout(function() {
  console.log('3 seconds has elapsed');
}, 3000);

// 等价写法二:箭头函数(更简洁)
setTimeout(() => {
  console.log('3 seconds has elapsed');
}, 3000);

在自己的作业文件里可以写一个通用的高阶函数,把 addNumbers 当作参数传入:

// operation 是一个函数,由调用方决定"怎么算"
function calculateWith(a, b, operation) {
  return operation(a, b);
}

console.log(calculateWith(5, 3, addNumbers));            // 8
console.log(calculateWith(5, 3, (x, y) => x - y));      // 2(箭头函数实参)

挑战 3:加入输入校验,让函数优雅处理非法输入

参考实现(对第 3、4 部分的关键函数做类型与范围检查):

function addNumbers(num1, num2) {
  if (typeof num1 !== 'number' || typeof num2 !== 'number') {
    throw new TypeError('addNumbers 的两个参数都必须是数字');
  }
  return num1 + num2;
}

function calculateTip(billAmount, tipPercentage = 15) {
  if (typeof billAmount !== 'number' || billAmount < 0) {
    throw new RangeError('billAmount 必须是非负数字');
  }
  if (typeof tipPercentage !== 'number' || tipPercentage < 0 || tipPercentage > 100) {
    throw new RangeError('tipPercentage 必须在 0 到 100 之间');
  }
  return (billAmount * tipPercentage) / 100;
}

// 测试:正常与非法输入各跑一遍
console.log(calculateTip(50));                          // 7.5
try {
  calculateTip(50, 120);                                // 触发 RangeError
} catch (e) {
  console.log(`已拦截非法输入: ${e.message}`);
}

校验的意义在于:默认参数只能兜住"忘传"的情况,兜不住"传错"(如 calculateTip('fifty') 会得到 NaN)。加一层 typeof 与范围判断后,函数对非法输入有明确的错误信息,调用方可以用 try/catch 捕获,这也是"代码质量"评分行中"结构良好"的具体体现。

自检清单与后续学习

提交前对照检查:

  • [ ] 6 个函数全部实现,命名使用 camelCasing,与作业要求完全一致;
  • [ ] greetPersoncalculateTip 的默认值分别是 'Hello'15,且放在必填参数之后;
  • [ ] addNumberscreateFullNamecalculateTipreturn 交还结果,而非 console.log
  • [ ] 测试代码覆盖"传默认值"与"显式传可选参数"两种调用方式;
  • [ ] 在浏览器开发者控制台(F12)中确认输出与预期一致;
  • [ ] (可选)完成至少一个加分挑战。

完成本作业后,建议按模块顺序继续:Making Decisions with JavaScript(用 if/elsecalculateTip 这类函数加上条件逻辑),以及 Arrays and Loops(讲义在"一周计划"中特别建议:用 map()filter() 等数组方法配合箭头函数做练习——那正是本课学到的"函数作为参数"模式的用武之地)。

相关文件索引

文件 说明
2-js-basics/2-functions-methods/assignment.md 本作业英文原文(Fun with Functions)
translations/bn/2-js-basics/2-functions-methods/assignment.md 本作业的孟加拉语译文(与原文内容一致)
2-js-basics/2-functions-methods/README.md 配套课程讲义:函数、参数、默认值、返回值、箭头函数原理
2-js-basics/README.md JavaScript 基础模块总览(4 课)
2-js-basics/1-data-types/README.md 前置课程:变量与数据类型
2-js-basics/4-arrays-loops/README.md 后续课程:数组与循环
sketchnotes/webdev101-js-functions.png 本课函数知识的手绘速记图(Sketchnote)
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 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
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.79 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
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
390