05.JavaScript 函数详解

05.JavaScript 函数详解

一、函数的定义与作用

1. 函数的基本概念

  • 函数是一段可重复使用的代码块
  • 用于执行特定任务或计算值
  • 提高代码的可重用性、可读性和可维护性

2. 函数的主要作用

  • 封装:将代码逻辑封装起来
  • 复用:多次调用同一段代码
  • 抽象:隐藏实现细节,暴露接口
  • 模块化:将程序分解为小的功能单元

二、函数的声明方式

1. 函数声明(Function Declaration)

// 基本语法
function functionName(parameters) {
    // 函数体
    return result;
}

// 示例
function greet(name) {
    return "Hello, " + name + "!";
}

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

特点

  • 存在函数提升(可以在声明前调用)
  • 有自己独立的 this 绑定
  • 属于全局作用域或函数作用域

2. 函数表达式(Function Expression)

// 基本语法
const functionName = function(parameters) {
    // 函数体
    return result;
};

// 示例
const multiply = function(x, y) {
    return x * y;
};

// 匿名函数表达式
const greet = function(name) {
    return "Hello, " + name;
};

// 有名称的函数表达式(推荐)
const factorial = function calcFactorial(n) {
    if (n <= 1) return 1;
    return n * calcFactorial(n - 1);
};

特点

  • 不存在函数提升
  • 可以作为参数传递
  • 可以立即调用

3. 箭头函数(Arrow Function,ES6+)

// 基本语法
const functionName = (parameters) => {
    // 函数体
    return result;
};

// 示例
const square = (x) => {
    return x * x;
};

// 简化形式
const square = x => x * x;                    // 单参数可省略括号
const add = (a, b) => a + b;                  // 单行可省略return和大括号
const greet = name => `Hello, ${name}`;       // 模板字符串
const noParam = () => console.log("Hello");   // 无参数需要括号

特点

  • 没有自己的 this,继承外层作用域的 this
  • 没有 arguments 对象
  • 不能用作构造函数(不能用 new
  • 没有 prototype 属性
  • 更简洁的语法

4. 构造函数(Function Constructor,不推荐)

// 语法:new Function(arg1, arg2, ..., functionBody)
const add = new Function('a', 'b', 'return a + b');
const greet = new Function('name', 'return "Hello, " + name');

console.log(add(2, 3));      // 5
console.log(greet("Alice")); // "Hello, Alice"

特点

  • 动态创建函数
  • 性能较差(每次都会解析)
  • 安全性问题(可能执行恶意代码)
  • 一般不建议使用

三、函数的调用方式

1. 直接调用

function sayHello() {
    console.log("Hello!");
}

sayHello();  // 直接调用

2. 作为方法调用(对象的方法)

const person = {
    name: "John",
    greet: function() {
        console.log("Hello, " + this.name);
    }
};

person.greet();  // "Hello, John"(this指向person对象)

3. 作为构造函数调用

function Person(name, age) {
    this.name = name;
    this.age = age;
}

const john = new Person("John", 25);  // 使用new关键字
console.log(john.name);  // "John"

4. 间接调用(call, apply, bind)

function introduce(greeting, punctuation) {
    console.log(greeting + ", I'm " + this.name + punctuation);
}

const person = { name: "Alice" };

// call - 立即调用,参数逐个传递
introduce.call(person, "Hello", "!");  // "Hello, I'm Alice!"

// apply - 立即调用,参数以数组传递
introduce.apply(person, ["Hi", "."]);  // "Hi, I'm Alice."

// bind - 返回新函数,稍后调用
const boundIntro = introduce.bind(person, "Hey");
boundIntro("!!");  // "Hey, I'm Alice!!"

5. 立即调用函数表达式(IIFE)

// 基本形式
(function() {
    console.log("立即执行");
})();

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

// 箭头函数形式
(() => {
    console.log("IIFE with arrow function");
})();

// 异步IIFE
(async function() {
    const data = await fetchData();
    console.log(data);
})();

6. 回调函数调用

// 作为参数传递
function processData(data, callback) {
    // 处理数据
    const result = data.toUpperCase();
    // 调用回调函数
    callback(result);
}

processData("hello", function(result) {
    console.log(result);  // "HELLO"
});

// 数组方法中的回调
const numbers = [1, 2, 3];
numbers.forEach(function(num) {
    console.log(num * 2);
});

四、函数参数详解

1. 形参与实参

// 形参(parameters):函数定义时声明的变量
function greet(name, greeting = "Hello") {
    console.log(greeting + ", " + name);
}

// 实参(arguments):函数调用时传递的值
greet("John", "Hi");  // "Hi, John"
greet("Alice");       // "Hello, Alice"(使用默认参数)

2. 参数传递机制

// 基本类型:按值传递
function changePrimitive(value) {
    value = 100;
}

let num = 50;
changePrimitive(num);
console.log(num);  // 50(不变)

// 引用类型:按引用传递(实际是传递引用的值)
function changeObject(obj) {
    obj.name = "Changed";
}

const person = { name: "Original" };
changeObject(person);
console.log(person.name);  // "Changed"(改变)

3. 默认参数(ES6)

function createUser(name, age = 18, isActive = true) {
    return {
        name,
        age,
        isActive
    };
}

console.log(createUser("John"));        // {name: "John", age: 18, isActive: true}
console.log(createUser("Alice", 25));   // {name: "Alice", age: 25, isActive: true}
console.log(createUser("Bob", 30, false)); // {name: "Bob", age: 30, isActive: false}

4. 剩余参数(Rest Parameters,ES6)

// 收集剩余参数为数组
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

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

// 与其他参数结合使用
function showInfo(name, age, ...hobbies) {
    console.log(`Name: ${name}, Age: ${age}`);
    console.log(`Hobbies: ${hobbies.join(", ")}`);
}

showInfo("John", 25, "reading", "gaming", "hiking");

5. arguments 对象

function showArguments() {
    console.log("参数数量:", arguments.length);
    console.log("所有参数:", arguments);

    // 转换为数组
    const argsArray = Array.from(arguments);
    // 或使用扩展运算符
    const argsArray2 = [...arguments];
}

showArguments(1, 2, 3, 4);

// 注意:箭头函数没有arguments对象
const showArgs = () => {
    // console.log(arguments);  // 错误!
};

五、函数的返回值

1. return 语句

// 返回一个值
function add(a, b) {
    return a + b;
}

// 提前返回
function divide(a, b) {
    if (b === 0) {
        return "Error: Division by zero";
    }
    return a / b;
}

// 返回对象
function createUser(name, age) {
    return {
        name: name,
        age: age,
        isAdult: age >= 18
    };
}

// 没有return或return空值,则返回undefined
function noReturn() {
    // 没有return语句
}
console.log(noReturn());  // undefined

function emptyReturn() {
    return;
}
console.log(emptyReturn());  // undefined

2. 返回函数(高阶函数)

// 函数工厂
function createMultiplier(factor) {
    return function(number) {
        return number * factor;
    };
}

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

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

3. 返回多个值

// 返回数组
function getMinMax(numbers) {
    return [Math.min(...numbers), Math.max(...numbers)];
}

const [min, max] = getMinMax([1, 5, 3, 9, 2]);
console.log(min, max);  // 1, 9

// 返回对象
function getUserInfo() {
    return {
        name: "John",
        age: 25,
        email: "john@example.com"
    };
}

const {name, age} = getUserInfo();
console.log(name, age);  // "John", 25

六、作用域与闭包

1. 函数作用域

// 全局变量
let globalVar = "I'm global";

function outerFunction() {
    // 局部变量(函数作用域)
    let outerVar = "I'm in outer function";

    function innerFunction() {
        // 可以访问外层变量
        console.log(outerVar);  // "I'm in outer function"
        console.log(globalVar); // "I'm global"

        // 自己的局部变量
        let innerVar = "I'm in inner function";
    }

    // console.log(innerVar);  // 错误!无法访问内层变量
    innerFunction();
}

outerFunction();

2. 闭包(Closure)

// 基本闭包
function createCounter() {
    let count = 0;  // 私有变量

    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter());  // 1
console.log(counter());  // 2
console.log(counter());  // 3

// 闭包的实际应用:数据封装
function createPerson(name) {
    let age = 0;  // 私有变量

    return {
        getName: function() {
            return name;
        },
        getAge: function() {
            return age;
        },
        setAge: function(newAge) {
            if (newAge >= 0) {
                age = newAge;
            }
        },
        birthday: function() {
            age++;
        }
    };
}

const person = createPerson("Alice");
person.setAge(25);
person.birthday();
console.log(person.getName(), person.getAge());  // "Alice", 26

七、特殊函数类型

1. 生成器函数(Generator Function,ES6)

function* numberGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = numberGenerator();
console.log(gen.next().value);  // 1
console.log(gen.next().value);  // 2
console.log(gen.next().value);  // 3
console.log(gen.next().done);   // true

// 无限生成器
function* infiniteSequence() {
    let i = 0;
    while (true) {
        yield i++;
    }
}

// 使用return提前结束
function* generatorWithReturn() {
    yield 1;
    return "结束";
    yield 2;  // 不会执行
}

2. 异步函数(Async Function,ES7)

// 基本语法
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Error:', error);
        throw error;
    }
}

// 使用方式
fetchData()
    .then(data => console.log(data))
    .catch(error => console.error(error));

// 异步函数表达式
const fetchUser = async function(userId) {
    // 异步操作
};

// 箭头异步函数
const fetchPost = async (postId) => {
    // 异步操作
};

3. 递归函数

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

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

// 斐波那契数列(带记忆化)
function fibonacci(n, memo = {}) {
    if (n in memo) return memo[n];
    if (n <= 2) return 1;
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
    return memo[n];
}

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

4. 纯函数(Pure Function)

// 纯函数:相同输入总是得到相同输出,无副作用
function pureAdd(a, b) {
    return a + b;
}

// 非纯函数:有副作用
let counter = 0;
function impureAdd(a) {
    counter++;  // 副作用:修改了外部状态
    return a + counter;
}

// 非纯函数:依赖于外部状态
function dependsOnExternal(x) {
    return x + Date.now();  // 每次结果不同
}

5. 柯里化函数(Currying)

// 普通函数
function addThree(a, b, c) {
    return a + b + c;
}

// 柯里化版本
function curriedAdd(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        };
    };
}

console.log(curriedAdd(1)(2)(3));  // 6

// 使用ES6简化
const 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));
            };
        }
    };
};

const curriedMultiply = curry((a, b, c) => a * b * c);
console.log(curriedMultiply(2)(3)(4));  // 24
console.log(curriedMultiply(2, 3)(4));  // 24

八、函数的方法与属性

1. 函数的属性

function example(a, b, c) {
    return a + b + c;
}

// 函数名
console.log(example.name);  // "example"

// 参数数量
console.log(example.length);  // 3

// 函数体字符串
console.log(example.toString());

2. call, apply, bind 方法

function introduce(greeting, punctuation) {
    console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}

const person = { name: "Alice" };

// call - 立即执行,参数逐个传递
introduce.call(person, "Hello", "!");

// apply - 立即执行,参数数组传递
introduce.apply(person, ["Hi", "."]);

// bind - 返回新函数,稍后执行
const boundIntro = introduce.bind(person);
setTimeout(() => boundIntro("Hey", "!!"), 1000);

// bind 部分参数
const sayHello = introduce.bind(person, "Hello");
sayHello("!");  // "Hello, I'm Alice!"

3. 自定义函数属性

// 缓存计算结果
function factorial(n) {
    if (!factorial.cache) {
        factorial.cache = {};
    }

    if (factorial.cache[n]) {
        console.log(`从缓存获取 ${n}!`);
        return factorial.cache[n];
    }

    if (n <= 1) return 1;

    const result = n * factorial(n - 1);
    factorial.cache[n] = result;
    console.log(`计算 ${n}!`);
    return result;
}

console.log(factorial(5));  // 计算并缓存
console.log(factorial(5));  // 从缓存获取

九、最佳实践

1. 函数命名规范

// 使用动词或动词短语
function getUserData() {}    // 获取用户数据
function calculateTotal() {} // 计算总计
function validateInput() {}  // 验证输入

// 使用小驼峰命名法
function processPayment() {}
function formatDateString() {}

2. 保持函数简洁(单一职责)

// 不好的写法:一个函数做太多事情
function processUser(user) {
    // 验证用户
    // 保存到数据库
    // 发送欢迎邮件
    // 记录日志
    // ...
}

// 好的写法:分解为多个函数
function validateUser(user) { /* ... */ }
function saveUserToDB(user) { /* ... */ }
function sendWelcomeEmail(user) { /* ... */ }
function logUserCreation(user) { /* ... */ }

function processUser(user) {
    validateUser(user);
    saveUserToDB(user);
    sendWelcomeEmail(user);
    logUserCreation(user);
}

3. 参数处理

// 使用对象参数提高可读性
function createUser(options) {
    const {
        name,
        age = 18,
        email,
        isActive = true
    } = options;

    // 使用参数
}

// 调用更清晰
createUser({
    name: "John",
    email: "john@example.com",
    age: 25
});

4. 错误处理

// 使用try-catch处理可能失败的操作
function safeParseJSON(jsonString) {
    try {
        return JSON.parse(jsonString);
    } catch (error) {
        console.error("JSON解析失败:", error.message);
        return null;
    }
}

// 参数验证
function divide(a, b) {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw new TypeError("参数必须是数字");
    }

    if (b === 0) {
        throw new Error("除数不能为零");
    }

    return a / b;
}

5. 文档注释

/**
 * 计算两个数的和
 * @param {number} a - 第一个数字
 * @param {number} b - 第二个数字
 * @returns {number} 两数之和
 * @throws {TypeError} 如果参数不是数字
 * @example
 * add(2, 3) // 返回 5
 */
function add(a, b) {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw new TypeError('参数必须是数字');
    }
    return a + b;
}

十、总结对比

1. 不同函数声明方式对比

特性 函数声明 函数表达式 箭头函数 构造函数
提升
名称 必需 可选 可选
this 动态 动态 词法 动态
arguments
new调用
prototype

2. 使用场景建议

  1. 函数声明:需要提升或需要多次调用的函数
  2. 函数表达式:作为回调函数或赋值给变量
  3. 箭头函数
    • 简短的回调函数
    • 需要继承外层 this 的场景
    • 需要更简洁的语法时
  4. 构造函数:创建多个相似对象
  5. 生成器函数:需要生成序列值
  6. 异步函数:处理异步操作

3. 性能注意事项

  1. 避免过度嵌套:深度嵌套的函数影响可读性和性能
  2. 合理使用闭包:闭包会保持外部变量引用,可能造成内存泄漏
  3. 缓存函数结果:对于计算密集型函数,考虑缓存结果
  4. 避免在循环中创建函数:每次循环都会创建新函数对象
// 不好的写法:在循环中创建函数
for (let i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

// 好的写法:使用IIFE或闭包
for (let i = 0; i < 10; i++) {
    (function(index) {
        setTimeout(function() {
            console.log(index);
        }, 1000);
    })(i);
}

// 或使用let的块级作用域
for (let i = 0; i < 10; i++) {
    setTimeout(() => console.log(i), 1000);
}

4. 现代JavaScript函数特性

  1. 默认参数:简化参数处理
  2. 剩余参数:替代 arguments 对象
  3. 展开运算符:简化数组和对象操作
  4. 解构参数:提取对象属性作为参数
  5. 尾调用优化(ES6):递归函数性能优化
// 现代函数示例
const createUser = ({ 
    name, 
    age = 18, 
    ...otherInfo 
}) => ({
    id: Date.now(),
    name,
    age,
    ...otherInfo,
    createdAt: new Date()
});

const user = createUser({
    name: "John",
    email: "john@example.com"
});
shi著

评论

  1. rowan
    Windows Chrome
    4 天前
    2025-12-06 0:22:53

    很详细

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇