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 类事件