函数是 JavaScript 的核心概念,掌握函数意味着真正理解 JavaScript 编程的精髓
1. 函数基础:减少重复代码的利器
1.1 函数的定义与调用
js
// 函数声明
function sayHello() {
console.log("Hello, World!");
}
// 函数调用
sayHello(); // 输出: Hello, World!
函数提升现象值得注意:
js
// 此处可以正常调用,因为函数声明会提升
test(); // 输出: "函数已调用"
function test() {
console.log("函数已调用");
}
1.2 参数与返回值
js
function calculateSum(a, b) {
return a + b;
}
const result = calculateSum(5, 3);
console.log(result); // 输出: 8
// 未传递参数的情况
function greet(name) {
if (name === undefined) {
return "Hello, stranger!";
}
return `Hello, ${name}!`;
}
console.log(greet()); // 输出: Hello, stranger!
2. 作用域与闭包:JavaScript 的"结界"
2.1 作用域链理解
js
var globalVar = "我是全局变量";
function outer() {
var outerVar = "我是外部函数变量";
function inner() {
var innerVar = "我是内部函数变量";
console.log(globalVar); // 可以访问
console.log(outerVar); // 可以访问
console.log(innerVar); // 可以访问
}
inner();
// console.log(innerVar); // 错误:无法访问内部函数变量
}
outer();
2.2 立即执行函数(IIFE)
js
// 传统写法 - 会污染全局作用域
var count = 0;
function increment() {
return ++count;
}
// IIFE写法 - 不会污染全局作用域
const counter = (function() {
let count = 0;
return {
increment: function() {
return ++count;
},
decrement: function() {
return --count;
},
getCount: function() {
return count;
}
};
})();
console.log(counter.getCount()); // 0
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
3. 函数的本质:一等公民的对象
3.1 函数作为值传递
js
// 函数赋值给变量
const multiply = function(a, b) {
return a * b;
};
// 函数作为参数传递
function operate(a, b, operation) {
return operation(a, b);
}
console.log(operate(5, 3, multiply)); // 输出: 15
// 函数作为返回值
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
console.log(double(5)); // 输出: 10
3.2 this 关键字的奥秘
js
const person = {
name: "张三",
sayName: function() {
console.log(this.name);
}
};
person.sayName(); // 输出: "张三"
// this 绑定丢失的情况
const sayName = person.sayName;
sayName(); // 输出: undefined (在严格模式下会报错)
4. 构造函数:创建对象的工厂
4.1 构造函数的使用
js
function Person(name, age) {
this.name = name;
this.age = age;
this.introduce = function() {
return `大家好,我是${this.name},今年${this.age}岁`;
};
}
// 使用 new 关键字创建实例
const person1 = new Person("李四", 25);
const person2 = new Person("王五", 30);
console.log(person1.introduce()); // 大家好,我是李四,今年25岁
console.log(person2.introduce()); // 大家好,我是王五,今年30岁
4.2 new.target 的应用
js
function Vehicle(type) {
if (!new.target) {
throw new Error("必须使用 new 关键字调用构造函数");
}
this.type = type;
}
// 正确使用
const car = new Vehicle("汽车");
// 错误使用
// const bike = Vehicle("自行车"); // 抛出错误
5. 递归:函数自我调用的艺术
5.1 经典递归案例:斐波那契数列
js
function fibonacci(n) {
if (n <= 0) return 0;
if (n === 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 优化版本:使用缓存
function fibonacciMemo(n, memo = {}) {
if (n in memo) return memo[n];
if (n <= 0) return 0;
if (n === 1) return 1;
memo[n] = fibonacciMemo(n - 1, memo) + fibonacciMemo(n - 2, memo);
return memo[n];
}
console.log(fibonacci(7)); // 13
console.log(fibonacciMemo(50)); // 12586269025 (优化后可以快速计算)
5.2 汉诺塔问题的递归解法
js
function hanoiTower(source, auxiliary, target, disks) {
if (disks === 1) {
console.log(`将盘子从 ${source} 移动到 ${target}`);
return;
}
// 将 n-1 个盘子从源柱移动到辅助柱
hanoiTower(source, target, auxiliary, disks - 1);
// 将最大的盘子从源柱移动到目标柱
console.log(`将盘子从 ${source} 移动到 ${target}`);
// 将 n-1 个盘子从辅助柱移动到目标柱
hanoiTower(auxiliary, source, target, disks - 1);
}
// 测试 3 个盘子的汉诺塔
hanoiTower('A', 'B', 'C', 3);
执行过程可视化:
css
A->C
A->B
C->B
A->C
B->A
B->C
A->C
6. 执行栈:理解函数调用的底层机制
通过下面的例子理解执行栈的工作原理:
html
<!DOCTYPE html>
<html>
<head>
<title>执行栈演示</title>
</head>
<body>
<script>
function A() {
console.log("A开始执行");
B();
console.log("A执行结束");
}
function B() {
console.log("B开始执行");
C();
console.log("B执行结束");
}
function C() {
console.log("C开始执行");
console.log("C执行结束");
}
console.log("全局开始");
A();
console.log("全局结束");
</script>
</body>
</html>
控制台输出顺序:
css
全局开始
A开始执行
B开始执行
C开始执行
C执行结束
B执行结束
A执行结束
全局结束
7. 高级技巧与最佳实践
7.1 尾递归优化
js
// 普通递归 - 可能导致栈溢出
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
// 尾递归优化版本
function factorialTail(n, accumulator = 1) {
if (n === 1) return accumulator;
return factorialTail(n - 1, n * accumulator);
}
console.log(factorial(5)); // 120
console.log(factorialTail(5)); // 120
7.2 函数柯里化
js
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
// 使用柯里化
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
总结
JavaScript 函数是语言的灵魂,从基础的参数传递到高级的闭包、递归等概念,理解函数意味着掌握了 JavaScript 编程的精髓。通过本文的讲解和代码示例,希望你能:
- 深入理解函数的作用域和闭包机制
- 掌握构造函数和原型链的关系
- 学会使用递归解决复杂问题
- 理解 JavaScript 的执行机制
函数的学习是一个循序渐进的过程,多实践、多思考,你会发现 JavaScript 函数的世界既深邃又美妙!