4JavaScript: The Good Parts

函数

函数是一等公民

函数字面量闭包模块模式this绑定

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第4章:函数(Functions)

一、导读

1.1 本章学习目标

第4章深入讲解 JavaScript 中最重要的特性——函数。函数是 JavaScript 的核心,它既是代码的执行单元,也是数据的载体。本章将揭示函数作为一等公民的全部能力。

本章的核心学习目标包括:

  • 理解函数的多种定义方式:函数声明、函数表达式、箭头函数。
  • 掌握闭包的概念和应用:理解词法作用域和闭包的原理。
  • 理解 this 绑定的规则:掌握四种 this 绑定方式。
  • 学会使用模块模式:利用闭包实现数据隐藏。
  • 理解回调函数:掌握异步编程的基础。

1.2 为什么第四章如此重要

函数是 JavaScript 的灵魂。函数作为一等公民,使得 JavaScript 支持函数式编程、高阶函数、闭包等强大特性。理解函数的本质,是写出优雅 JavaScript 代码的关键。

1.3 核心问题引导

函数声明和函数表达式有什么区别?

闭包是什么?它为什么如此强大?

this 的绑定规则有哪些?箭头函数的 this 有何不同?

如何利用闭包实现模块模式?

回调函数和异步编程有什么关系?


二、核心概念详解

4.1 函数字面量

javascript
// 函数声明(会被提升)
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 函数是一等公民

javascript
// 函数可以赋值给变量
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;  // 2

4.3 闭包(Closure)

闭包是函数和它声明时的词法环境的组合。闭包允许函数访问其外部作用域中的变量。

javascript
// 闭包的基本形式
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();  // TypeError

4.4 this 绑定

javascript
// 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 模块模式

javascript
// 模块模式(使用 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 回调与高阶函数

javascript
// 回调函数
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 函数式编程实战

javascript
// 柯里化
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)

javascript
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 方法。


4/9函数
0%