JavaScript函数:编程之基石

180 阅读4分钟

一、函数的定义与调用

在JavaScript中,函数可以通过多种方式定义,包括函数声明、函数表达式和箭头函数。每种方式都有其独特的语法和适用场景。

函数声明

函数声明是最常见的定义函数的方式,它使用function关键字来声明一个函数,并指定函数名和参数列表。

function greet(name) {
  return `Hello, ${name}!`;
}

函数声明会被提升到当前作用域的顶部,这意味着可以在声明之前调用该函数。

函数表达式

函数表达式是将一个匿名函数或具名函数赋值给一个变量的方式。函数表达式不会被提升,因此必须在定义之后才能调用。

const greet = function(name) {
  return `Hello, ${name}!`;
};

箭头函数

箭头函数是ES6引入的一种新的函数定义方式,它使用=>符号来定义函数,并且具有更简洁的语法。

const greet = (name) => `Hello, ${name}!`;

箭头函数没有自己的this,它会捕获其所在上下文的this值。

二、函数的参数传递

JavaScript函数的参数传递方式有两种:按值传递和按引用传递。

按值传递

基本数据类型(如数字、字符串、布尔值)在函数调用时是按值传递的。这意味着函数内部对参数的修改不会影响到外部变量。

function changeValue(x) {
  x = 10;
}

let num = 5;
changeValue(num);
console.log(num); // 输出 5

按引用传递

对象类型(如数组、对象)在函数调用时是按引用传递的。这意味着函数内部对参数的修改会影响到外部变量。

function changeObject(obj) {
  obj.name = 'Alice';
}

let person = { name: 'Bob' };
changeObject(person);
console.log(person.name); // 输出 'Alice'

三、闭包

闭包是JavaScript中一个非常强大的特性,它允许函数记住并访问其词法作用域,即使这个函数在其词法作用域之外执行。

function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}

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

闭包常用于创建私有变量和实现模块化。

四、高阶函数

高阶函数是指接受函数作为参数或返回一个函数的函数。高阶函数在JavaScript中非常常见,并且是实现函数式编程的重要工具。

接受函数作为参数

function map(array, callback) {
  const result = [];
  for (let i = 0; i < array.length; i++) {
    result.push(callback(array[i]));
  }
  return result;
}

const numbers = [1, 2, 3, 4];
const doubled = map(numbers, x => x * 2);
console.log(doubled); // 输出 [2, 4, 6, 8]

返回一个函数

function createAdder(x) {
  return function(y) {
    return x + y;
  };
}

const add5 = createAdder(5);
console.log(add5(3)); // 输出 8

五、函数柯里化

函数柯里化是一种将接受多个参数的函数转换为一系列接受单一参数的函数的技术。柯里化可以提高函数的复用性和灵活性。

function 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));
      };
    }
  };
}

function add(x, y) {
  return x + y;
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)); // 输出 3

六、异步函数

异步函数是指在执行过程中不会阻塞主线程的函数。JavaScript中的异步函数通常通过回调函数、Promise和async/await来实现。

回调函数

回调函数是最早的异步编程方式,通过将一个函数作为参数传递给另一个函数,在异步操作完成时调用该函数。

function fetchData(callback) {
  setTimeout(() => {
    callback('Data fetched');
  }, 1000);
}

fetchData(data => {
  console.log(data); // 输出 'Data fetched'
});

Promise

Promise是一种更现代的异步编程方式,它表示一个异步操作的最终完成或失败状态。

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data fetched');
    }, 1000);
  });
}

fetchData().then(data => {
  console.log(data); // 输出 'Data fetched'
});

async/await

async/await是基于Promise的语法糖,它使得异步代码看起来更像同步代码,从而提高代码的可读性和可维护性。

async function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data fetched');
    }, 1000);
  });
}

async function main() {
  const data = await fetchData();
  console.log(data); // 输出 'Data fetched'
}

main();

七、函数式编程

函数式编程是一种编程范式,它将计算视为数学函数的求值,并避免改变状态和可变数据。JavaScript中的函数式编程特性包括纯函数、不可变数据和高阶函数等。

纯函数

纯函数是指相同的输入总是产生相同的输出,并且没有副作用的函数。

function add(x, y) {
  return x + y;
}

console.log(add(1, 2)); // 输出 3
console.log(add(1, 2)); // 输出 3

不可变数据

不可变数据是指一旦创建就不能被修改的数据。函数式编程鼓励使用不可变数据来避免副作用。

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(x => x * 2);
console.log(doubled); // 输出 [2, 4, 6, 8]
console.log(numbers); // 输出 [1, 2, 3, 4]