函数(Function)

函数是可复用的代码块。JavaScript 中函数是一等公民:可以被赋值、作为参数传递、作为返回值返回。


函数的创建方式

1) 函数声明(Function Declaration)

function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5

特点:会被“函数提升”(可在声明之前调用)。

2) 函数表达式(Function Expression)

const multiply = function (a, b) {
return a * b;
};
console.log(multiply(2, 3)); // 6

特点:不会被提升到可用状态,需先定义后使用。可命名/匿名,用于回调更灵活。

3) 箭头函数(Arrow Function)

const divide = (a, b) => a / b;
// 多语句需显式 return
const sum = (arr) => {
let s = 0;
for (const n of arr) s += n;
return s;
};

特点:

  • 没有自己的 this/arguments/super/new.target,this 由词法作用域决定
  • 不能作为构造函数(不可 new)
  • 适合回调、数组方法等简洁场景

参数与返回值

参数默认值

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

剩余参数(Rest Parameters)

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

展开语法(Spread)

const arr = [1, 2, 3];
console.log(sumAll(...arr)); // 6

返回值

function parse(json, fallback = null) {
try {
return JSON.parse(json);
} catch {
return fallback;
}
}

this 绑定与箭头函数差异

const obj = {
x: 42,
normal() {
return function () {
return this.x;
};
},
arrow() {
return () => this.x;
},
};
const f1 = obj.normal();
console.log(f1()); // undefined(非严格模式下可能是全局)
console.log(f1.call(obj)); // 42(显式绑定)
const f2 = obj.arrow();
console.log(f2()); // 42(箭头函数捕获词法 this)

结论:需要稳定的 this 指向时,优先使用箭头函数作为回调;作为对象方法且需要动态 this 时用常规方法写法。


作用域与闭包(Closure)

闭包是指函数可以“记住并访问”其词法作用域中的变量,即使函数在其定义作用域之外执行。

function makeCounter() {
let count = 0; // 私有变量
return function () {
count += 1;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2

常见用途:私有状态、函数工厂、缓存。


回调、Promise 与 async/await

// 回调风格
function fetchDataCb(url, cb) {
setTimeout(() => cb(null, { url, ok: true }), 100);
}
// Promise 风格
function fetchData(url) {
return new Promise((resolve) => {
setTimeout(() => resolve({ url, ok: true }), 100);
});
}
// async/await
async function load() {
const data = await fetchData("/api");
return data.ok;
}

建议:新代码优先使用 Promise 与 async/await,避免回调地狱。


高阶函数与函数式技巧

高阶函数(参数或返回值是函数)

function once(fn) {
let called = false;
let result;
return function (...args) {
if (!called) {
called = true;
result = fn.apply(this, args);
}
return result;
};
}
const init = once(() => console.log("init"));
init();
init(); // 第二次不执行

柯里化(Currying)

const add3 = (a) => (b) => (c) => a + b + c;
console.log(add3(1)(2)(3)); // 6

组合(compose/pipe)

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

函数重载的替代方案

JS 无原生重载,常用模式:

function format(input) {
if (typeof input === "number") return input.toFixed(2);
if (Array.isArray(input)) return input.join(",");
if (input && typeof input === "object") return JSON.stringify(input);
return String(input);
}

性能与最佳实践

  • 避免在热路径里创建闭包;必要时将函数提升到外层复用。
  • 频繁调用的小函数可内联到上层逻辑(权衡可读性)。
  • 箭头函数简洁但不要滥用;对象方法、构造函数使用常规函数。
  • 明确返回值与异常路径,I/O 函数优先返回 Promise。

常见陷阱

arguments 与剩余参数

function demo() {
console.log(arguments.length); // 类数组,不是数组
}
function better(...args) {
console.log(args.length); // 真数组
}

this 丢失

const obj2 = {
x: 1,
getX() {
return this.x;
},
};
const getX = obj2.getX;
console.log(getX()); // undefined(严格模式)
console.log(getX.call(obj2)); // 1

小结

函数是 JS 抽象的核心。掌握多种定义方式、this 绑定、闭包与异步控制流,可以写出可读、可维护且高性能的代码。