JavaScript 函数
学习函数定义、参数、返回值 · 难度:进阶 · +15XP
JavaScript 函数 — 代码复用的核心
一、什么是函数?为什么它如此重要?
函数是一段可重复使用的代码块,用于执行特定的任务。你可以把函数想象成一台"机器":它接收输入(参数),进行处理(函数体),然后返回输出(返回值)。
为什么函数重要?函数是编程中最强大的抽象工具:
- 避免重复代码 — 写一次,到处调用(DRY 原则:Don't Repeat Yourself)
- 模块化 — 将复杂问题拆解为多个小函数,每个函数只做一件事
- 可测试性 — 独立的函数更容易编写单元测试
- 代码可读性 — 好的函数名本身就是注释,让代码自解释
在现代前端开发中,函数更是无处不在:事件处理、数据转换、API 调用、React 组件……几乎一切都是函数。
二、JavaScript 函数的四种定义方式
| 方式 | 语法示例 | 特点 |
|---|---|---|
| 函数声明 | function add(a,b){ return a+b; } | 会提升(hoisting),可在定义前调用 |
| 函数表达式 | const add = function(a,b){ return a+b; }; | 不会提升,赋值后才可用 |
| 箭头函数 | const add = (a,b) => a + b; | 简洁,没有自己的 this/arguments |
| 立即执行函数 | (function(){ console.log("IIFE"); })(); | 定义后立即执行,常用于隔离作用域 |
三、参数与返回值 — 函数的"输入"与"输出"
函数的参数是函数与外界交互的接口。JS 函数的参数非常灵活:
- 默认参数:
function greet(name = "游客")— 不传参数时使用默认值 - 剩余参数:
function sum(...nums)— 将多个参数收集到一个数组中 - 解构参数:
function print({name, age})— 直接从对象中提取属性
四、详细代码示例(逐行注释)
// ======== 1. 函数声明 ========
// 最经典的定义方式,会被提升到作用域顶部
function greet(name) { // name 是形参(parameter)
return "你好," + name + "!"; // return 将结果返回给调用者
}
console.log(greet("小明")); // "小明" 是实参(argument)
// 输出:你好,小明!
// ======== 2. 函数表达式 ========
// 将匿名函数赋值给变量
const sayBye = function(name) {
return "再见," + name + "!";
};
console.log(sayBye("小红")); // 输出:再见,小红!
// ======== 3. 箭头函数(ES6,最常用) ========
// 语法更简洁,适合短小的回调函数
const add = (a, b) => a + b; // 单行直接返回
const square = x => x * x; // 只有一个参数可以省略括号
const greet2 = name => "你好," + name; // 一行代码就能完成
console.log(add(3, 5)); // 输出:8
// 多行箭头函数需要用花括号和 return
const calculateArea = (width, height) => {
const area = width * height; // 计算面积
return area; // 必须显式 return
};
// ======== 4. 默认参数 ========
function order(item, quantity = 1) { // quantity 默认值为 1
return "订购 " + quantity + " 个" + item;
}
console.log(order("苹果")); // 输出:订购 1 个苹果
console.log(order("香蕉", 3)); // 输出:订购 3 个香蕉
// ======== 5. 剩余参数 ========
function sum(...numbers) { // ... 收集所有参数到数组中
let total = 0;
for (let n of numbers) { // 遍历数组
total += n; // 累加
}
return total;
}
console.log(sum(1, 2, 3, 4)); // 输出:10
// ======== 6. 回调函数 ========
// 将函数作为参数传递给另一个函数
function processData(data, callback) {
const result = data.map(callback); // 对每个元素调用 callback
return result;
}
const doubled = processData([1, 2, 3], x => x * 2);
console.log(doubled); // 输出:[2, 4, 6]
// ======== 7. 闭包 — 函数返回函数 ========
function createCounter(start = 0) {
let count = start; // count 被内部函数"记住"
return function() { // 返回一个函数
count++; // 每次调用 count 加 1
return count;
};
}
const counter = createCounter(10);
console.log(counter()); // 输出:11
console.log(counter()); // 输出:12
console.log(counter()); // 输出:13
// ======== 8. 纯函数 vs 非纯函数 ========
// 纯函数:相同输入永远得到相同输出,无副作用
function pureAdd(a, b) { return a + b; }
// 非纯函数:依赖或修改了外部状态
let total = 0;
function impureAdd(n) { total += n; return total; }
五、箭头函数 vs 普通函数 — 全面对比
| 对比项 | 普通函数 | 箭头函数 |
|---|---|---|
| 语法 | function foo() {} | () => {} |
| this 绑定 | 有自己的 this(动态绑定) | 没有自己的 this(继承外层) |
| arguments 对象 | 有 | 没有 |
| 可作为构造函数 | 可以(new) | 不可以 |
| 适合场景 | 对象方法、构造函数 | 回调、数组方法链 |
| prototype 属性 | 有 | 没有 |
六、实践任务
- 编写一个函数
isEven(n),接收一个数字,返回它是否是偶数(boolean) - 编写箭头函数
max(a, b),返回两个数中的较大值 - 编写函数
factorial(n),用递归计算 n 的阶乘(n! = n * (n-1) * ... * 1) - 创建一个计数器函数(使用闭包),每次调用返回递增的数字
- 编写函数
filterAdults(users),接收用户数组 [{name, age}],返回年龄 >= 18 的用户