Skip to content

1.2 函数与作用域

函数是 JavaScript 的核心概念,它是可重用的代码块。作用域决定了变量的可访问范围。

一、函数定义

1. 函数声明(命名函数)

javascript
function greet(name) {
    return "Hello, " + name + "!";
}

// 函数声明会被提升(可在声明前调用)
console.log(greet("张三")); // "Hello, 张三!"

2. 函数表达式(匿名函数)

javascript
const greet = function(name) {
    return "Hello, " + name + "!";
};

// 不会被提升
// console.log(greet("张三")); // 报错

3. 箭头函数(ES6)

javascript
const greet = (name) => {
    return "Hello, " + name + "!";
};

// 简化形式(单行返回)
const greet = name => "Hello, " + name + "!";

// 无参数
const sayHello = () => "Hello!";

// 多参数
const add = (a, b) => a + b;

4. 立即执行函数(IIFE)

javascript
(function() {
    console.log("立即执行");
})();

// 带参数
(function(name) {
    console.log("Hello, " + name);
})("张三");

// 箭头函数写法
(() => {
    console.log("立即执行");
})();

二、参数

1. 默认参数

javascript
function greet(name = "张三") {
    return "Hello, " + name + "!";
}

console.log(greet());       // "Hello, 张三!"
console.log(greet("李四")); // "Hello, 李四!"

2. 剩余参数

javascript
function sum(...nums) {
    return nums.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3));     // 6
console.log(sum(1, 2, 3, 4));  // 10

3. 解构参数

javascript
function createUser({ name, age, city = "北京" }) {
    return { name, age, city };
}

const user = createUser({ name: "张三", age: 25 });
console.log(user); // { name: "张三", age: 25, city: "北京" }

4. 参数验证

javascript
function divide(a, b) {
    if (typeof a !== "number" || typeof b !== "number") {
        throw new Error("参数必须是数字");
    }
    if (b === 0) {
        throw new Error("除数不能为零");
    }
    return a / b;
}

三、返回值

javascript
// 显式返回
function add(a, b) {
    return a + b;
}

// 隐式返回(undefined)
function noReturn() {
    console.log("没有返回值");
}

// 返回对象(注意括号)
const getUser = () => ({ name: "张三", age: 25 });

// 提前返回
function login(username, password) {
    if (!username || !password) {
        return false;
    }
    // 验证逻辑...
    return true;
}

四、作用域

1. 作用域链

javascript
let global = "全局变量";

function outer() {
    let outer = "外层变量";

    function inner() {
        let inner = "内层变量";
        console.log(global); // 可访问全局
        console.log(outer);  // 可访问外层
        console.log(inner);  // 可访问内层
    }

    inner();
    // console.log(inner); // 报错:无法访问内层变量
}

outer();

2. 块级作用域

javascript
// var:函数作用域
function testVar() {
    if (true) {
        var x = 10;
    }
    console.log(x); // 10(var 会泄露到函数作用域)
}

// let/const:块级作用域
function testLet() {
    if (true) {
        let y = 10;
    }
    console.log(y); // 报错:y 在块外不可访问
}

3. 作用域示例

javascript
let globalVar = "全局";

function testScope() {
    let functionVar = "函数局部";

    if (true) {
        let blockVar = "块级局部";
        console.log(globalVar);    // ✅
        console.log(functionVar);  // ✅
        console.log(blockVar);     // ✅
    }

    console.log(globalVar);        // ✅
    console.log(functionVar);      // ✅
    // console.log(blockVar);      // ❌ 报错
}

五、闭包

定义

闭包是指函数能够访问其外部作用域的变量,即使外部函数已经执行完毕。

示例

javascript
function outer() {
    let count = 0;

    return function() {
        count++;
        console.log(count);
    };
}

const counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3

// 每次调用 counter,都可以访问 outer 中的 count 变量

应用场景

javascript
// 1. 数据私有化
function createCounter() {
    let count = 0;
    return {
        increment: () => ++count,
        decrement: () => --count,
        getCount: () => count,
    };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 1
// counter.count 无法直接访问

// 2. 函数工厂
function makeGreeter(greeting) {
    return function(name) {
        console.log(greeting + ", " + name + "!");
    };
}

const sayHello = makeGreeter("Hello");
const sayHi = makeGreeter("Hi");

sayHello("张三"); // "Hello, 张三!"
sayHi("李四");   // "Hi, 李四!"

六、this 关键字

1. 普通函数

javascript
// 严格模式下 this 为 undefined
function test() {
    "use strict";
    console.log(this); // undefined
}
test();

// 非严格模式
function test2() {
    console.log(this); // window(浏览器)
}
test2();

2. 对象方法

javascript
const person = {
    name: "张三",
    greet: function() {
        console.log(this.name); // "张三"(指向 person)
    }
};
person.greet();

3. 箭头函数

javascript
const person = {
    name: "张三",
    greet: () => {
        console.log(this.name); // undefined(箭头函数没有绑定 this)
    },
    greet2: function() {
        const arrow = () => {
            console.log(this.name); // "张三"(继承外层 this)
        };
        arrow();
    }
};
person.greet();  // undefined
person.greet2(); // "张三"

4. call/apply/bind

javascript
function greet(greeting = "你好") {
    console.log(greeting + ", " + this.name + "!");
}

const person = { name: "张三" };

// call
greet.call(person, "你好"); // "你好, 张三!"

// apply(参数以数组形式传递)
greet.apply(person, ["您好"]); // "您好, 张三!"

// bind(返回新函数)
const boundGreet = greet.bind(person);
boundGreet("哈喽"); // "哈喽, 张三!"

七、高阶函数

函数作为参数或返回值的函数。

1. 函数作为参数

javascript
function operate(a, b, operation) {
    return operation(a, b);
}

function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

console.log(operate(5, 3, add));       // 8
console.log(operate(5, 3, multiply)); // 15

2. 函数作为返回值

javascript
function multiplyBy(n) {
    return function(x) {
        return x * n;
    };
}

const double = multiplyBy(2);
const triple = multiplyBy(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

3. 常用高阶函数

javascript
const nums = [1, 2, 3, 4, 5];

// map:映射
const doubled = nums.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// filter:过滤
const evens = nums.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]

// reduce:归约
const sum = nums.reduce((total, n) => total + n, 0);
console.log(sum); // 15

// forEach:遍历
nums.forEach(n => console.log(n));

// find:查找
const found = nums.find(n => n > 3);
console.log(found); // 4

// some:是否存在满足条件的
const hasEven = nums.some(n => n % 2 === 0);
console.log(hasEven); // true

// every:是否都满足条件
const allPositive = nums.every(n => n > 0);
console.log(allPositive); // true

八、递归

函数调用自身。

javascript
// 阶乘
function factorial(n) {
    if (n === 0 || n === 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

console.log(factorial(5)); // 120

// 斐波那契数列
function fibonacci(n) {
    if (n === 0) return 0;
    if (n === 1) return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

console.log(fibonacci(10)); // 55

九、总结

  • 函数声明会被提升,表达式不会
  • 箭头函数没有自己的 this,继承外层
  • 闭包让函数能访问外部作用域变量
  • 高阶函数让代码更简洁、可复用
  • 递归要设置终止条件,避免栈溢出