第8章:函数(Functions)
一、导读
1.1 本章学习目标
第8章深入讲解JavaScript的函数。本章核心目标:
- 掌握函数的各种定义方式:声明、表达式、箭头函数
- 理解闭包:函数和作用域的结合
- 掌握this关键字:在不同上下文中的指向
- 学会使用call、apply、bind
- 理解高阶函数
1.2 为什么本章如此重要
函数是JavaScript中最重要的特性。JavaScript的函数是"一等公民",可以像值一样传递和使用。闭包是JavaScript最强大的特性之一,理解闭包是掌握JavaScript的关键。
1.3 核心问题引导
函数声明和函数表达式有什么区别?
箭头函数和普通函数有什么本质区别?
闭包是什么?有什么实际应用?
this在箭头函数和普通函数中有什么不同?
二、核心概念详解
8.1 函数定义
javascript
// 函数声明(有提升)
function add(a, b) {
return a + b;
}
// 函数表达式
const multiply = function(a, b) {
return a * b;
};
// 箭头函数
const subtract = (a, b) => a - b;
const square = x => x * x;
// 默认参数
function greet(name = "Guest") {
console.log(`Hello, ${name}!`);
}
// 剩余参数
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4)); // 108.2 闭包
javascript
// 闭包:函数记住并访问其词法作用域
function counter() {
let count = 0;
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
};
}
const c = counter();
c.increment();
c.increment();
console.log(c.getCount()); // 2
// 闭包实现私有变量
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount > balance) throw new Error("Insufficient funds");
balance -= amount;
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// 闭包在循环中的应用
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2(let有块级作用域)
}
// 使用IIFE创建模块
const module = (function() {
let privateVar = 0;
return {
increment() { privateVar++; },
getValue() { return privateVar; }
};
})();8.3 this关键字
javascript
// 全局上下文
console.log(this); // 浏览器中是window
// 对象方法
let obj = {
name: "Alice",
greet() {
console.log(`Hello, ${this.name}`);
},
greetArrow: () => {
console.log(`Hello, ${this.name}`); // this指向外层
}
};
obj.greet(); // "Hello, Alice"
obj.greetArrow(); // "Hello, undefined"
// call, apply, bind
function introduce(greeting) {
console.log(`${greeting}, I'm ${this.name}`);
}
let person = {name: "Bob"};
introduce.call(person, "Hi"); // "Hi, I'm Bob"
introduce.apply(person, ["Hey"]); // "Hey, I'm Bob"
let boundIntroduce = introduce.bind(person);
boundIntroduce("Hello"); // "Hello, I'm Bob"
// 在事件处理中
class Button {
constructor(element) {
this.clickCount = 0;
element.addEventListener('click', () => {
this.clickCount++; // 箭头函数保持this
});
}
}8.4 高阶函数
javascript
// 接受函数作为参数
function applyOperation(a, b, operation) {
return operation(a, b);
}
console.log(applyOperation(5, 3, (a, b) => a + b)); // 8
// 返回函数
function multiplier(factor) {
return function(x) {
return x * factor;
};
}
const triple = multiplier(3);
console.log(triple(5)); // 15
// 函数组合
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const transform = pipe(addOne, double, square);
console.log(transform(3)); // (3+1)*2=8, 8*8=64
// 柯里化
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...args2) {
return curried.apply(this, [...args, ...args2]);
};
};
}
const curriedAdd = curry((a, b, c) => a + b + c);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6三、实战案例
3.1 函数式数据处理
javascript
const data = [
{name: "Alice", department: "Engineering", salary: 90000},
{name: "Bob", department: "Marketing", salary: 75000},
{name: "Charlie", department: "Engineering", salary: 95000},
{name: "David", department: "Marketing", salary: 80000},
{name: "Eve", department: "Engineering", salary: 85000}
];
// 函数式数据处理管道
const result = data
.filter(emp => emp.department === "Engineering")
.map(emp => ({...emp, bonus: emp.salary * 0.1}))
.sort((a, b) => b.salary - a.salary);
console.log(result);
// 分组函数
function groupBy(arr, keyFn) {
return arr.reduce((acc, item) => {
const key = keyFn(item);
(acc[key] = acc[key] || []).push(item);
return acc;
}, {});
}
const byDept = groupBy(data, emp => emp.department);
console.log(byDept);四、本章小结
第8章深入讲解了JavaScript函数:定义方式、闭包、this关键字、call/apply/bind、高阶函数。函数是JavaScript的核心,闭包是最强大的特性。
五、思考与练习
思考题
函数声明的提升和变量的提升有什么区别?
闭包会导致内存泄漏吗?如何避免?
箭头函数没有自己的this,那它的this是什么?
什么是函数式编程?JavaScript支持函数式编程吗?
编程练习
实现一个memoize函数(带缓存的函数)。
实现一个debounce函数(防抖)。
使用闭包实现一个简单的状态机。