首页
/ Web-Dev-For-Beginners JS 基础精讲:从函数声明到高阶函数的 JavaScript 方法与函数完全指南

Web-Dev-For-Beginners JS 基础精讲:从函数声明到高阶函数的 JavaScript 方法与函数完全指南

2026-09-06 21:54:07作者:冯梦姬Eddie

本文基于 Web-Dev-For-Beginners 课程 2-js-basics 模块的「Methods and Functions」一节,系统讲解 JavaScript 中函数(Function)与方法(Method)的核心概念:声明与调用、参数与实参、默认值、返回值、函数作为参数(高阶函数),以及匿名函数与箭头函数三种语法风格的选择策略。读完本文,你不仅能独立完成课程配套的「Fun with Functions」练习和 mathUtils.js 工具库挑战,还能在仓库内的 Terrarium、打字游戏、太空射击游戏等真实项目源码中印证这些概念的实际落地方式。

Web-Dev-For-Beginners JavaScript 函数课程手绘思维导图,出自 Tomomi Imura 的 sketchnote 系列

为什么需要函数:把重复逻辑打包成可复用组件

重复编写同一段代码是编程中最常见的烦恼之一。函数(function)解决了这个问题:它允许你把代码打包成一个可复用的块。课程的类比很形象——函数就像让福特装配线革命化的标准件:一旦你做出了一个可靠的组件,就可以在任何需要的地方使用,而不必从零重建。

函数让你把代码片段捆绑起来,在程序的任何位置重复调用。与其把同样的逻辑到处复制粘贴,不如一次创建函数、随时调用。这样做有两个直接收益:

  • 代码保持整洁、组织有序;
  • 后续维护更容易——试想如果一段逻辑散落在代码库的 20 个地方,修改时会有多大的维护成本。

课程还特别强调命名即文档:给函数起一个描述性的名字至关重要。当你看到 cancelTimer() 时,立刻就能明白它是干什么的,就像清楚标注的按钮告诉你点击后会发生什么一样。

函数(Function)与方法(Method)的区别

课程原文明确指出:你在整个课程中其实一直在使用方法console.log() 就是一个方法——本质上是属于 console 对象的一个函数。关键区别在于:

  • 函数是独立存在的代码块;
  • 方法是挂载在对象上的函数,调用时通过对象访问(对象.方法())。

许多开发者在口语中会混用这两个术语,但理解「方法依附于对象」这一点,是后续学习 DOM API(document.getElementById())、数组方法(map()filter())的基础。

课程挑战(Challenge):能否用一句话概括函数和方法的区别?答案要点:函数独立存在,方法是属于某个对象、通过该对象调用的函数。

创建和调用函数

语法结构

函数定义遵循一致的模式:

function nameOfFunction() { // function definition
  // function definition/body
}

逐部分拆解:

  • function 关键字告诉 JavaScript「嘿,我在创建一个函数!」;
  • nameOfFunction 是函数描述性名称的位置;
  • 圆括号 () 是放置参数(parameters)的位置;
  • 花括号 {} 包含调用该函数时实际执行的代码。

一个最小示例:displayGreeting

function displayGreeting() {
  console.log('Hello, world!');
}

这个函数把 "Hello, world!" 打印到控制台。定义之后,你可以按需使用任意多次。

要**执行(调用)**函数,写出函数名后紧跟括号:

// calling our function
displayGreeting();

执行这一行时,函数体内所有代码都会被执行,浏览器控制台显示 "Hello, world!"。你甚至可以反复调用同一个函数。

一个重要的 JavaScript 特性:JavaScript 允许你先定义函数、后调用它,也可以先写调用、后写定义(函数声明会被提升,hoisting),JavaScript 引擎会处理执行顺序。

命名最佳实践

课程给出的三条实用建议:

  1. 给函数起清晰、有描述性的名字——未来的你会感谢现在的自己;
  2. 多词名称使用驼峰命名法(camelCasing),如 calculateTotal 而不是 calculate_total
  3. 让每个函数专注于做好一件事。

实战印证:仓库中 Terrarium 项目的解决方案 script.js 恰好体现了「一次定义、多次复用」——dragElement 函数只定义了一次(script.jsfunction dragElement(terrariumElement) {...}),随后被连续调用 14 次,让 plant1plant14 这 14 个盆栽元素全部获得拖拽能力。如果不用函数,就得复制 14 份几乎相同的拖拽代码。

向函数传递信息:参数(Parameters)与实参(Arguments)

displayGreeting 只输出固定的 "Hello, world!",能力有限。参数让函数变得灵活:它们是占位符,每次调用时都可以填入不同的值,同一个函数因此能在不同调用中处理不同信息。

定义函数时,把参数列在括号里,多个参数用逗号分隔:

function name(param, param2, param3) {

}

更新后的问候函数接受一个名字参数:

function displayGreeting(name) {
  const message = `Hello, ${name}!`;
  console.log(message);
}

注意这里用反引号(`)和 ${} 把变量直接拼进字符串——这是模板字符串(template literal),是混入变量构建字符串的便捷方式。

调用时传入任意名字即可:

displayGreeting('Christopher');
// 运行时输出 "Hello, Christopher!"

JavaScript 会把字符串 'Christopher' 赋给 name 参数,再构造出 "Hello, Christopher!" 这条个性化消息。参数可以是任何类型:字符串、数字、布尔值、对象,甚至函数。

实战印证4-typing-game/solution/index.js 中的 renderQuote 函数就是一个典型的带参数函数——它接受 quote 字符串参数,内部用模板字符串逐词渲染成带高亮类的 <span>;而 index.jsmessages.success 更是把「函数作为配置项」和模板字符串结合了起来:success: (seconds) => \🎉 Congratulations! You finished in ${seconds.toFixed(2)} seconds.``。

参数的信息流

课程用流程图总结了参数的工作方式,用文字表述就是:

函数调用 displayGreeting('Alice')
  → 参数绑定:name = 'Alice'
  → 函数体执行:`Hello, ${name}!` 模板拼接
  → 产出结果:'Hello, Alice!'

概念核对:参数(parameter)是定义函数时声明的占位符;实参(argument)是调用时实际传入的值。多传的实参会被函数忽略;传少了则对应参数为 undefined

默认值:让参数可选

如果想让某些参数变成可选的,就用默认值(default values)

以「允许自定义问候词、未指定时回退到 Hello」为例,用等号即可设置默认值,和普通变量赋值写法一致:

function displayGreeting(name, salutation='Hello') {
  console.log(`${salutation}, ${name}`);
}

这里 name 仍然是必需的,而 salutation 在调用者没有提供时使用备用值 'Hello'

于是同一个函数有两种调用方式:

displayGreeting('Christopher');
// 输出 "Hello, Christopher"(使用默认值)

displayGreeting('Christopher', 'Hi');
// 输出 "Hi, Christopher"(使用自定义值)

第一次调用中 JavaScript 使用默认的 "Hello";第二次则使用自定义的 "Hi"。这种灵活性让函数能适应不同场景。

进阶提示:默认参数让函数更易用——调用者可以用合理默认值快速上手,需要定制时再传参,同时保持向后兼容。

实战印证:太空游戏的 app.js 把默认参数用到了极致:

const $ = (sel, ctx = document) => ctx.querySelector(sel);
const $$ = (sel, ctx = document) => Array.from(ctx.querySelectorAll(sel));

ctx 的默认值是 document,因此 $('#myCanvas') 默认在全局查找;需要限定作用域时才传第二个参数。从源码结构看,这是「默认参数 + 箭头函数 + 返回值」三个概念的组合应用。

返回值:让函数把结果交还给你

前面的函数只是往控制台打印消息。如果希望函数计算出某个结果并交还给你呢?这就是**返回值(return values)**的用途:函数可以把一个值交还给调用方,你可以把它存进变量,或在代码的其他地方使用。

return 关键字加上要返回的值:

return myVariable;

关键行为:当函数执行到 return 语句时,会立即停止运行,并把该值送回给调用者。

把问候函数改成「返回消息」而不是「打印消息」:

function createGreetingMessage(name) {
  const message = `Hello, ${name}`;
  return message;
}

现在这个函数构造消息并把它交还给你。用变量接收返回值:

const greetingMessage = createGreetingMessage('Christopher');

此时 greetingMessage 包含 "Hello, Christopher",你可以把它用在任何地方——显示在网页上、写进邮件、传给另一个函数。函数如果没有 return 语句,会隐式返回 undefined

关键洞察:返回值的函数更通用,因为由调用方决定如何处置结果,这让代码更具模块化和可复用性。

实战印证typing-game 的 index.jsconst getRandomQuote = () => quotes[Math.floor(Math.random() * quotes.length)]; 是一个箭头函数形式的「纯计算、返回结果」函数:它不直接操作 UI,而是把随机选中的一句名言交还给调用者,由 startGame 决定拿它做什么。返回值让「取数据」和「用数据」分离,正是课程所说模块化思想的体现。

函数作为函数的参数:高阶函数

函数可以被当作参数传给另一个函数。这个概念初看复杂,却是非常强大的特性,支撑起灵活的编程模式。

这种模式最典型的应用场景是表达「当某事发生时,执行另一件事」:比如「定时器结束时运行这段代码」「用户点击按钮时调用这个函数」。

课程用内置函数 setTimeout 演示——它等待指定时间后执行一段代码,你需要告诉它「执行什么」,正好适合传入一个函数:

function displayDone() {
  console.log('3 seconds has elapsed');
}
// timer value is in milliseconds
setTimeout(displayDone, 3000);

注意我们传给 setTimeout 的是 displayDone不带括号)——我们并没有自己调用它,而是把它「移交」给 setTimeout,说「3 秒后调用它」。如果写成 setTimeout(displayDone(), 3000),则是立即调用并把返回值(这里是 undefined)传过去,行为完全不同。

实战印证一(事件回调):Terrarium 的 dragElement 内部就是高阶用法的教科书案例:terrariumElement.onpointerdown = pointerDrag; 把内部函数 pointerDrag 交给元素作为指针按下事件的回调;随后 document.onpointermove = elementDrag;document.onpointerup = stopElementDrag; 又分别注册了移动与松手回调。

实战印证二(Promise 回调):浏览器扩展项目的 index.js 中,axios.get(...).then((response) => { ... }) 把处理响应的箭头函数交给 Promise 的 then 方法,这正是「当某事(数据到达)发生时,执行另一件事(渲染结果)」的同款模式。此外 index.jslet num = (element) => element > closestNum; co2Scale.findIndex(num); 也是把函数作为参数传给数组方法 findIndex——从源码结构看,这是高阶函数在浏览器环境中的日常形态。

匿名函数

有时你只为一次用途需要函数,不想为它命名——只用一次的函数何必额外占用一个名字?

JavaScript 允许创建匿名函数(anonymous functions):没有名字、直接在使用处定义的函数。

用匿名函数重写定时器示例:

setTimeout(function() {
  console.log('3 seconds has elapsed');
}, 3000);

效果完全相同,但函数直接定义在 setTimeout 调用内部,省去了独立的函数声明。

箭头函数(Arrow Functions)

现代 JavaScript 提供了更简洁的函数写法——箭头函数,使用 =>(长得像箭头)。它允许你省略 function 关键字,写出更紧凑的代码。

setTimeout(() => {
  console.log('3 seconds has elapsed');
}, 3000);

结构拆解:() 是放参数的位置(本例为空),然后是箭头 =>,最后是花括号包裹的函数体。功能与之前完全一致,只是语法更简洁。

实战印证:打字游戏项目整体就是箭头函数风格。index.js 中连续定义了四个箭头函数工具:getRandomQuote(取随机引语)、renderQuote(渲染引语)、highlightWord(高亮当前词);index.jshandleTyping 处理每次击键。而太空游戏的 app.js 则用 (() => { ... })()(立即执行函数表达式,IIFE)把整个游戏封装在独立的箭头函数作用域内——从源码结构看,这是一种避免污染全局命名空间的常用组织手法。

何时选择哪种写法

课程给出的实用准则:

风格 形态 特点 适用场景
传统命名函数 function name() {} 具名、可被提升(hoisted) 需要在多处复用的函数
箭头函数 const name = () => {} 简洁、现代风格 短回调、工具函数,现代代码库中最常见
匿名函数 function() {} 无名字、就地定义 一次性使用的事件处理器等

如果函数会被多次使用,就给它命名并独立定义;如果只是某个单一用途,考虑匿名或内联箭头函数。传统语法与箭头函数都是有效选择,只是箭头函数在现代代码库中更为普遍。

现代趋势:因为简洁,箭头函数正成为许多开发者的默认选择,但传统函数依然有其位置。

项目内函数用法的横向对照

仓库里几个项目的解决方案,恰好覆盖本节的每个知识点,适合作为自检清单:

知识点 文档示例 仓库印证(文件与位置)
一次定义、多次调用 displayGreeting() script.jsdragElement 调用 14 次
参数 + 模板字符串 displayGreeting(name) index.jsrenderQuote(quote)
默认参数 salutation='Hello' app.js(sel, ctx = document)
返回值 createGreetingMessage index.jsgetRandomQuote
函数作为参数 setTimeout(displayDone, 3000) index.jsfindIndex(num)script.js:事件回调赋值
匿名/箭头回调 setTimeout(() => {...}, 3000) index.js.then((response) => {...})index.jsaddEventListener("click", startGame)
闭包(延伸) script.js 注释明确说明:dragElement 内部函数捕获外层 pos1~pos4 状态,正是后续课程「闭包」概念的雏形

实战练习:Fun with Functions 作业

课程配套作业 assignment.md(「Fun with Functions」)要求创建一个名为 functions-practice.js 的文件,分五个部分巩固本节全部概念:

Part 1:基础函数

  1. sayHello:不接受任何参数,仅向控制台打印 "Hello!"。
  2. introduceYourself:接受 name 参数,打印类似 "Hi, my name is [name]" 的消息。

Part 2:带默认参数的函数

  1. greetPerson:接受两个参数——name(必需)和 greeting(可选,默认 "Hello"),打印 "[greeting], [name]!" 格式的消息。

Part 3:返回值的函数

  1. addNumbers:接受 num1num2 两个参数,返回它们的和。
  2. createFullName:接受 firstNamelastName 参数,返回拼合后的完整姓名(单个字符串)。

Part 4:综合运用

  1. calculateTip:接受 billAmount(必需)和 tipPercentage(可选,默认 15)两个参数,计算并返回小费金额。

Part 5:测试你的函数

为每个函数添加调用,并用 console.log() 展示结果。作业给出的示例测试调用:

// 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 打开浏览器开发者控制台,即可看到 console.log() 的输出。

评分标准(Rubric)摘要

作业定义了五个维度的评分标准:函数创建(6 个函数是否语法正确、命名规范)、参数与默认值(必需/可选参数与默认值是否符合规格)、返回值(该返回的函数正确返回,不该返回的只做动作)、代码质量(组织清晰、命名有意义、缩进规范)、测试(所有函数都有恰当测试且结果展示清晰)。

加分挑战(可选)

  1. 把其中一个函数改写成箭头函数版本;
  2. 创建一个接受另一个函数作为参数的函数(类似课程中 setTimeout 的示例);
  3. 增加输入校验,让函数能妥善处理无效输入。

进阶挑战:用 Agent 模式构建 mathUtils.js

课程还设计了一个 GitHub Copilot Agent 模式的挑战:创建一个演示本节全部概念的数学工具库 mathUtils.js,包含以下函数:

  1. add:接受两个参数,返回它们的和;
  2. multiply:带默认参数值(第二个参数默认为 1);
  3. square:箭头函数,接受一个数字并返回其平方;
  4. calculate:接受一个函数作为参数和两个数字,然后对该两个数字应用这个函数;
  5. 用合适的测试用例演示调用每个函数。

参照课程的概念,一个符合规格的实现大致如下(自行在本地环境编写运行):

// mathUtils.js — 演示本节全部函数概念

// 1. 基础参数 + 返回值
function add(a, b) {
  return a + b;
}

// 2. 默认参数
function multiply(a, b = 1) {
  return a * b;
}

// 3. 箭头函数
const square = (n) => n * n;

// 4. 函数作为参数(高阶函数)
function calculate(fn, a, b) {
  return fn(a, b);
}

// 5. 测试用例
console.log(add(2, 3));              // 5
console.log(multiply(4));           // 4(b 使用默认值 1)
console.log(square(5));              // 25
console.log(calculate(add, 2, 3));   // 5
console.log(calculate(multiply, 4, 5)); // 20

其中 calculate 就是「函数作为参数」概念的最小实用形态:它本身不关心具体运算,只负责「应用你交给我的那个函数」,调用方通过传入不同的函数决定行为——这与 Terrarium 中「事件发生 → 执行回调」的模式在结构上是同一件事。

复习与进阶学习路径

课程建议重点复习箭头函数——它们在代码库中的使用率持续上升。练习方法:先按传统语法写一个函数,再把它改写成箭头函数语法。

按照课程给出的进阶节奏,一个合理的练习路径是:

  • 起步(几十分钟内):写一个返回你最喜欢的数字的简单函数;写一个两参数求和函数;把一个传统函数改写成箭头函数;完成「函数 vs 方法」的一句话挑战。
  • 本周:构建上面的 mathUtils.js 工具库;完成「Fun with Functions」作业的全部 6 个函数;写一个以另一个函数为参数的函数;练习默认参数与模板字符串返回值。
  • 进阶:把写过的重复代码重构为可复用函数;只用函数(不用全局变量)搭一个小计算器;用箭头函数配合 map()filter() 练习数组方法;研究高阶函数与函数式编程概念;在闭包与作用域(仓库 Terrarium 的 dragElement 是天然的闭包示例)上深入,构建大量使用函数组合的项目。

小结:函数工具箱

把本节的知识浓缩成一张对照表:

概念 关键语法 核心要点
函数声明 function name() {} 描述性命名(camelCase)、一次定义、多次复用、可提升
方法 obj.method() 挂在对象上的函数,如 console.log()
参数 function f(a, b) {} 占位符;调用时传入的实参可以是任意类型
默认值 function f(a, b = 1) {} 参数可选、保持向后兼容
返回值 return value; 遇到 return 立即退出并交还值;调用方决定用途,模块化更强
高阶函数 setTimeout(fn, ms)findIndex(fn) 函数可作参数传递,表达「当 X 发生时做 Y」
匿名函数 function() {} 一次性用途、无需命名
箭头函数 const f = (a) => ... 简洁现代,短回调首选

函数的收益可以归结为四点:代码可复用、组织更清晰、更易测试、支持模块化设计。正如课程结语所言:函数是更大程序的构建块——你未来构建的每一个应用都会用函数来组织、复用和结构化代码。掌握把逻辑打包成可复用组件的能力,就是进入模块化编程世界的入口。

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

项目优选

收起
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
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391