Skip to content

JavaScript 函数

函数声明

函数声明式

提升到作用域顶部,可在声明前调用。

js
function greet(name) {
  return `Hello, ${name}`;
}
greet("Tom"); // 'Hello, Tom'

函数表达式

赋值给变量,不会提升,只能在声明后调用。

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

箭头函数

更简洁的写法,没有自己的 this

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

// 多行写法
const add = (a, b) => {
  const result = a + b;
  return result;
};

立即执行函数(IIFE)

定义后立即执行,常用于隔离作用域。

js
(function () {
  const msg = "IIFE";
  console.log(msg);
})();

函数参数

默认参数

js
function greet(name = "World") {
  return `Hello, ${name}`;
}
greet(); // 'Hello, World'
greet("Tom"); // 'Hello, Tom'

剩余参数

将多余参数收集为数组。

js
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6

参数解构

js
function display({ name, age = 18 }) {
  console.log(name, age);
}
display({ name: "Tom" }); // Tom 18

函数特性

arguments 对象

非箭头函数中可用,包含所有传入参数(类数组)。

js
function sum() {
  return Array.from(arguments).reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6

函数长度(length)

返回函数形参个数(不含默认参数和剩余参数)。

js
function fn(a, b, c = 1) {}
fn.length; // 2

函数名(name)

js
function foo() {}
foo.name; // 'foo'

const bar = function baz() {};
bar.name; // 'baz'

高阶函数

接收函数作为参数,或返回一个函数。

函数作为参数

js
function repeat(fn, n) {
  for (let i = 0; i < n; i++) fn(i);
}
repeat(console.log, 3); // 0 1 2

函数作为返回值

js
function multiply(x) {
  return (y) => x * y;
}
const double = multiply(2);
double(5); // 10

闭包

函数记住并访问其词法作用域,即使在作用域外执行。

js
function counter() {
  let count = 0;
  return {
    increment: () => ++count,
    get: () => count,
  };
}
const c = counter();
c.increment(); // 1
c.increment(); // 2
c.get(); // 2

纯函数

相同输入始终返回相同输出,无副作用。

js
// 纯函数 ✅
const add = (a, b) => a + b;

// 非纯函数 ❌(依赖外部变量)
let base = 10;
const addBase = (a) => a + base;

函数柯里化

将多参数函数转换为一系列单参数函数。

js
const curry = (fn) => {
  return function curried(...args) {
    if (args.length >= fn.length) return fn(...args);
    return (...more) => curried(...args, ...more);
  };
};

const add = curry((a, b, c) => a + b + c);
add(1)(2)(3); // 6
add(1, 2)(3); // 6

函数组合

将多个函数组合成一个函数,从右到左执行。

js
const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((v, f) => f(v), x);

const double = (x) => x * 2;
const addOne = (x) => x + 1;
const square = (x) => x * x;

const transform = compose(square, addOne, double);
transform(3); // square(addOne(double(3))) = square(7) = 49

记忆化(Memoization)

缓存函数结果,避免重复计算。

js
function memoize(fn) {
  const cache = new Map();
  return function (...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const fib = memoize(function (n) {
  if (n <= 1) return n;
  return fib(n - 1) + fib(n - 2);
});
fib(40); // 快速计算

异步函数

回调函数

js
function fetchData(callback) {
  setTimeout(() => callback("data"), 1000);
}
fetchData((data) => console.log(data)); // 'data'

Promise

js
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("data"), 1000);
  });
}
fetchData().then((data) => console.log(data));

async / await

js
async function fetchData() {
  const res = await fetch("https://api.example.com/data");
  const data = await res.json();
  return data;
}

常见技巧

函数防抖(debounce)

延迟执行,频繁触发时只执行最后一次。

js
function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// 场景:搜索框输入
const onSearch = debounce((val) => console.log(val), 500);

函数节流(throttle)

限制执行频率,固定时间内只执行一次。

js
function throttle(fn, interval) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= interval) {
      last = now;
      fn.apply(this, args);
    }
  };
}

// 场景:滚动事件
const onScroll = throttle(() => console.log("scroll"), 200);

函数重载模拟

js
function handle(...args) {
  if (args.length === 1) return `单参数: ${args[0]}`;
  if (args.length === 2) return `双参数: ${args[0]}, ${args[1]}`;
}
handle("a"); // '单参数: a'
handle("a", "b"); // '双参数: a, b'

总结

小提示

  • 优先使用箭头函数处理简单逻辑,函数声明处理复杂逻辑
  • 保持函数单一职责,一个函数只做一件事
  • 纯函数更易测试和维护,尽量减少副作用
  • 防抖用于输入类事件,节流用于滚动/resize 类事件