Skip to content

JS 函数

函数的概念

函数是一段可重用的代码块,用于执行特定的任务。函数可以接受输入参数,执行操作,并返回结果。在 JavaScript 中,函数是一等公民,这意味着它们可以像其他值一样被传递、赋值给变量、作为参数传递给其他函数,以及作为函数的返回值。

函数的分类

1. 命名函数

命名函数是有名称的函数,可以通过名称调用:

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

console.log(add(5, 10)); // 输出: 15

2. 匿名函数

匿名函数是没有名称的函数,通常作为值赋给变量或作为参数传递给其他函数:

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

console.log(add(5, 10)); // 输出: 15

// 作为参数传递
setTimeout(function() {
  console.log('Hello, World!');
}, 1000);

3. 箭头函数

箭头函数是 ES6 引入的一种更简洁的函数语法:

javascript
const add = (a, b) => a + b;

console.log(add(5, 10)); // 输出: 15

// 单个参数
const square = x => x * x;
console.log(square(5)); // 输出: 25

// 无参数
const greet = () => 'Hello, World!';
console.log(greet()); // 输出: Hello, World!

// 多行函数体
const multiply = (a, b) => {
  const result = a * b;
  return result;
};
console.log(multiply(5, 10)); // 输出: 50

4. 立即执行函数表达式 (IIFE)

IIFE 是一种立即执行的函数表达式:

javascript
(function() {
  console.log('This function executes immediately');
})();

// 带参数的 IIFE
(function(name) {
  console.log(`Hello, ${name}!`);
})('John');

// 箭头函数形式
(() => {
  console.log('Arrow function IIFE');
})();

5. 生成器函数

生成器函数是 ES6 引入的一种特殊函数,可以暂停和恢复执行:

javascript
function* generateNumbers() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = generateNumbers();
console.log(generator.next().value); // 输出: 1
console.log(generator.next().value); // 输出: 2
console.log(generator.next().value); // 输出: 3
console.log(generator.next().value); // 输出: undefined

6. 异步函数

异步函数是 ES2017 引入的一种函数,可以使用 asyncawait 关键字处理异步操作:

javascript
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 fetching data:', error);
    throw error;
  }
}

fetchData()
  .then(data => console.log(data))
  .catch(error => console.error(error));

函数的特性

1. 函数作用域

在函数内部声明的变量具有函数作用域,只能在函数内部访问:

javascript
function myFunction() {
  const localVar = 'Local';
  console.log(localVar); // 输出: Local
}

myFunction();
// console.log(localVar); // 错误:无法访问局部变量

2. 闭包

闭包是指函数能够访问其词法作用域之外的变量:

javascript
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

3. 函数提升

函数声明会被提升到作用域的顶部,因此可以在声明之前调用:

javascript
console.log(add(5, 10)); // 输出: 15(函数声明被提升)

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

// 函数表达式不会被提升
// console.log(subtract(10, 5)); // 错误:subtract 未定义

const subtract = function(a, b) {
  return a - b;
};

4. this 关键字

在函数内部,this 关键字指向调用该函数的对象:

javascript
const person = {
  name: 'John',
  greet: function() {
    return `Hello, ${this.name}!`;
  }
};

console.log(person.greet()); // 输出: Hello, John!

// 箭头函数中的 this
const person2 = {
  name: 'John',
  greet: () => {
    return `Hello, ${this.name}!`; // 箭头函数中的 this 指向外部作用域
  }
};

console.log(person2.greet()); // 输出: Hello, undefined!

函数的应用

1. 模块化

使用函数可以将代码组织成模块,提高代码的可维护性:

javascript
// 数学模块
const math = {
  add: function(a, b) {
    return a + b;
  },
  subtract: function(a, b) {
    return a - b;
  },
  multiply: function(a, b) {
    return a * b;
  },
  divide: function(a, b) {
    if (b === 0) {
      throw new Error('Division by zero');
    }
    return a / b;
  }
};

console.log(math.add(5, 10)); // 输出: 15
console.log(math.subtract(10, 5)); // 输出: 5

2. 回调函数

函数可以作为参数传递给其他函数,用于处理异步操作或事件:

javascript
// 异步操作
function fetchData(url, callback) {
  setTimeout(() => {
    const data = { id: 1, name: 'John' };
    callback(null, data);
  }, 1000);
}

fetchData('https://api.example.com/data', function(error, data) {
  if (error) {
    console.error('Error:', error);
  } else {
    console.log('Data:', data);
  }
});

// 事件处理
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log('Button clicked!');
});

3. 高阶函数

高阶函数是接受函数作为参数或返回函数的函数:

javascript
// 接受函数作为参数
function map(array, callback) {
  const result = [];
  for (let i = 0; i < array.length; i++) {
    result.push(callback(array[i], i, array));
  }
  return result;
}

const numbers = [1, 2, 3, 4, 5];
const doubled = map(numbers, function(num) {
  return num * 2;
});
console.log(doubled); // 输出: [2, 4, 6, 8, 10]

// 返回函数
function createMultiplier(multiplier) {
  return function(num) {
    return num * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 输出: 10
console.log(triple(5)); // 输出: 15

4. 递归

递归是指函数调用自身的过程:

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

console.log(factorial(5)); // 输出: 120

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

console.log(fibonacci(10)); // 输出: 55

函数的最佳实践

1. 函数命名

  • 函数名应该具有描述性,清晰地表达函数的功能
  • 使用驼峰命名法(camelCase)
  • 动词开头表示动作
javascript
// 好的函数名
function calculateSum(a, b) {
  return a + b;
}

function getUserData(id) {
  // 获取用户数据
}

// 不好的函数名
function sum(a, b) {
  return a + b;
}

function data(id) {
  // 获取用户数据
}

2. 函数长度

  • 函数应该保持简短,通常不超过 20-30 行
  • 一个函数应该只做一件事
  • 对于复杂的功能,将其分解为多个小函数

3. 参数数量

  • 函数参数数量应该合理,通常不超过 3-4 个
  • 对于多个参数,考虑使用对象作为参数
javascript
// 好的做法
function createUser({ name, email, age }) {
  // 创建用户
}

createUser({ name: 'John', email: 'john@example.com', age: 30 });

// 不好的做法
function createUser(name, email, age, address, phone) {
  // 创建用户
}

4. 错误处理

  • 函数应该处理可能的错误
  • 使用 try/catch 语句捕获错误
  • 返回适当的错误信息
javascript
function divide(a, b) {
  if (b === 0) {
    throw new Error('Division by zero');
  }
  return a / b;
}

try {
  console.log(divide(10, 0));
} catch (error) {
  console.error(error.message);
}

5. 文档

  • 为函数添加注释,说明函数的用途、参数和返回值
  • 使用 JSDoc 格式
javascript
/**
 * 计算两个数的和
 * @param {number} a - 第一个数
 * @param {number} b - 第二个数
 * @returns {number} 两个数的和
 */
function add(a, b) {
  return a + b;
}

小结

函数是 JavaScript 中的核心概念之一,它允许我们将代码组织成可重用的块。JavaScript 提供了多种类型的函数,包括命名函数、匿名函数、箭头函数、生成器函数和异步函数。理解函数的特性和应用,是学习 JavaScript 的基础。在实际开发中,应该遵循最佳实践,编写清晰、简洁、可维护的函数。