第4章:函数(Functions)
一、导读
1.1 本章学习目标
第4章深入讲解 JavaScript 中最重要的特性——函数。函数是 JavaScript 的核心,它既是代码的执行单元,也是数据的载体。本章将揭示函数作为一等公民的全部能力。
本章的核心学习目标包括:
- 理解函数的多种定义方式:函数声明、函数表达式、箭头函数。
- 掌握闭包的概念和应用:理解词法作用域和闭包的原理。
- 理解 this 绑定的规则:掌握四种 this 绑定方式。
- 学会使用模块模式:利用闭包实现数据隐藏。
- 理解回调函数:掌握异步编程的基础。
1.2 为什么第四章如此重要
函数是 JavaScript 的灵魂。函数作为一等公民,使得 JavaScript 支持函数式编程、高阶函数、闭包等强大特性。理解函数的本质,是写出优雅 JavaScript 代码的关键。
1.3 核心问题引导
函数声明和函数表达式有什么区别?
闭包是什么?它为什么如此强大?
this 的绑定规则有哪些?箭头函数的 this 有何不同?
如何利用闭包实现模块模式?
回调函数和异步编程有什么关系?
二、核心概念详解
4.1 函数字面量
// 函数声明(会被提升)
function add(a, b) {
return a + b;
}
// 函数表达式
var multiply = function(a, b) {
return a * b;
};
// 命名函数表达式(用于递归和调试)
var factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
// 立即执行函数表达式(IIFE)
var result = (function() {
var secret = 42;
return secret * 2;
})();4.2 函数是一等公民
// 函数可以赋值给变量
var greet = function(name) { return "Hello, " + name; };
// 函数可以作为参数
function apply(fn, x) { return fn(x); }
apply(Math.abs, -5); // 5
// 函数可以作为返回值
function makeAdder(x) {
return function(y) { return x + y; };
}
var add5 = makeAdder(5);
add5(3); // 8
// 函数可以拥有属性
function counter() {
counter.count = (counter.count || 0) + 1;
return counter.count;
}
counter(); // 1
counter(); // 2
counter.count; // 24.3 闭包(Closure)
闭包是函数和它声明时的词法环境的组合。闭包允许函数访问其外部作用域中的变量。
// 闭包的基本形式
function outer() {
var x = 10;
function inner() {
console.log(x); // inner 可以访问 outer 的变量
}
return inner;
}
var fn = outer();
fn(); // 10(即使 outer 已经返回,x 仍然存在)
// 闭包实现计数器
function createCounter(initial) {
var count = initial || 0;
return {
increment: function() { return ++count; },
decrement: function() { return --count; },
getCount: function() { return count; }
};
}
var c = createCounter(10);
c.increment(); // 11
c.increment(); // 12
c.decrement(); // 11
c.getCount(); // 11
// count 变量对外部不可见
// 闭包实现私有方法
function Container(spec) {
var privateValue = spec.value;
function privateMethod() {
return "private: " + privateValue;
}
return {
getPublicValue: function() { return privateValue; },
setPublicValue: function(v) { privateValue = v; },
callPrivate: function() { return privateMethod(); }
};
}
var container = Container({value: 42});
container.getPublicValue(); // 42
container.callPrivate(); // "private: 42"
// container.privateMethod(); // TypeError4.4 this 绑定
// 1. 默认绑定(全局或 undefined)
function foo() {
console.log(this); // 非严格模式:window;严格模式:undefined
}
foo();
// 2. 隐式绑定(对象方法调用)
var obj = {
name: "Alice",
greet: function() {
console.log("Hello, " + this.name);
}
};
obj.greet(); // "Hello, Alice"
// 3. 显式绑定(call/apply/bind)
var greetFn = obj.greet;
greetFn.call({name: "Bob"}); // "Hello, Bob"
greetFn.apply({name: "Charlie"}); // "Hello, Charlie"
var boundGreet = greetFn.bind({name: "Dave"});
boundGreet(); // "Hello, Dave"
// 4. new 绑定
function Person(name) {
this.name = name; // this 指向新创建的对象
}
var p = new Person("Eve");
p.name; // "Eve"
// 箭头函数没有自己的 this
var obj2 = {
name: "Alice",
greet: function() {
// 箭头函数继承外层的 this
setTimeout(() => {
console.log("Hello, " + this.name);
}, 100);
}
};4.5 模块模式
// 模块模式(使用 IIFE)
var MathUtils = (function() {
// 私有变量和方法
var PI = 3.14159265358979;
function validateNumber(n) {
if (typeof n !== "number") {
throw new TypeError("Expected a number");
}
}
// 公共 API
return {
circleArea: function(radius) {
validateNumber(radius);
return PI * radius * radius;
},
circleCircumference: function(radius) {
validateNumber(radius);
return 2 * PI * radius;
},
degreesToRadians: function(degrees) {
validateNumber(degrees);
return degrees * PI / 180;
}
};
})();
MathUtils.circleArea(5); // 78.53981633974483
// MathUtils.PI; // undefined(私有)4.6 回调与高阶函数
// 回调函数
function fetchData(url, callback) {
// 模拟异步操作
setTimeout(function() {
var data = {url: url, content: "Hello"};
callback(null, data);
}, 1000);
}
fetchData("/api/data", function(error, data) {
if (error) {
console.error(error);
} else {
console.log(data);
}
});
// 高阶函数
function map(array, fn) {
var result = [];
for (var i = 0; i < array.length; i++) {
result.push(fn(array[i], i, array));
}
return result;
}
function filter(array, predicate) {
var result = [];
for (var i = 0; i < array.length; i++) {
if (predicate(array[i], i, array)) {
result.push(array[i]);
}
}
return result;
}
function reduce(array, fn, initial) {
var acc = initial;
for (var i = 0; i < array.length; i++) {
acc = fn(acc, array[i], i, array);
}
return acc;
}
// 使用
var numbers = [1, 2, 3, 4, 5];
map(numbers, function(n) { return n * 2; }); // [2, 4, 6, 8, 10]
filter(numbers, function(n) { return n > 3; }); // [4, 5]
reduce(numbers, function(sum, n) { return sum + n; }, 0); // 15三、实战案例
3.1 函数式编程实战
// 柯里化
function curry(fn) {
var arity = fn.length;
return function curried() {
var args = Array.prototype.slice.call(arguments);
if (args.length >= arity) {
return fn.apply(this, args);
}
return function() {
var moreArgs = Array.prototype.slice.call(arguments);
return curried.apply(this, args.concat(moreArgs));
};
};
}
var add = curry(function(a, b, c) { return a + b + c; });
add(1)(2)(3); // 6
add(1, 2)(3); // 6
add(1)(2, 3); // 6
// 组合
function compose() {
var fns = Array.prototype.slice.call(arguments);
return function(x) {
return fns.reduceRight(function(acc, fn) {
return fn(acc);
}, x);
};
}
var double = function(x) { return x * 2; };
var square = function(x) { return x * x; };
var addOne = function(x) { return x + 1; };
var transform = compose(addOne, square, double);
transform(3); // 37 ((3*2)^2 + 1)3.2 记忆化(Memoization)
function memoize(fn) {
var cache = {};
return function() {
var key = JSON.stringify(arguments);
if (cache[key] === undefined) {
cache[key] = fn.apply(this, arguments);
}
return cache[key];
};
}
// 使用记忆化优化递归
var fibonacci = memoize(function(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
fibonacci(50); // 瞬间完成(不使用记忆化会非常慢)四、本章小结
第4章深入讲解了 JavaScript 函数的核心知识:
函数是一等公民:可以赋值、传参、返回。
闭包:函数和词法环境的组合,实现数据隐藏。
this 绑定:四种规则——默认、隐式、显式、new。
模块模式:利用闭包实现封装。
高阶函数:map、filter、reduce 等函数式编程工具。
函数是 JavaScript 最强大的特性。掌握闭包和高阶函数,你就能写出优雅、简洁、强大的代码。
五、思考与练习
思考题
闭包的内存开销是什么?如何避免内存泄漏?
箭头函数和普通函数有哪些区别?
如何实现一个通用的 curry 函数?
模块模式和类有什么区别?
回调地狱是什么?如何解决?
编程练习
实现 once 函数:确保函数只执行一次。
实现 pipe 函数:从左到右组合函数。
实现事件发射器:使用闭包实现 on/emit/off 方法。