JavaScript Function 函数
简介
函数是 JavaScript 中的一等公民(First-class Citizen),意味着函数可以像变量一样被传递、赋值和返回。函数用于封装可复用的代码块。
函数定义方式
JavaScript 中有三种主要的函数定义方式。
1. 函数声明(Function Declaration)
使用 function 关键字直接定义函数。
function greet(name) {
return 'Hello, ' + name + '!';
}
console.log(greet('Alice')); // Hello, Alice!
特点:函数提升(Hoisting)
函数声明会被提升到作用域顶部,可以在定义之前调用。
console.log(add(2, 3)); // 5(可以在定义前调用)
function add(a, b) {
return a + b;
}
2. 函数表达式(Function Expression)
将函数赋值给变量。
const greet = function(name) {
return 'Hello, ' + name + '!';
};
console.log(greet('Bob')); // Hello, Bob!
// 也可以给函数命名(用于递归等场景)
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1);
};
特点:没有提升
函数表达式不会被提升,必须在定义之后才能调用。
console.log(multiply(2, 3)); // TypeError: multiply is not a function
const multiply = function(a, b) {
return a * b;
};
3. 箭头函数(Arrow Function,ES6)
ES6 引入的简洁语法。
// 基本语法
const greet = (name) => {
return 'Hello, ' + name + '!';
};
// 单个参数时,括号可以省略
const double = x => x * 2;
// 函数体只有一条 return 语句时,可以省略花括号和 return
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
console.log(double(5)); // 10
特点:没有自己的 this、arguments、super、new.target
// 箭头函数继承外层作用域的 this
const obj = {
value: 10,
getValue: function() {
// 普通函数:this 指向调用者(obj)
return this.value;
},
getValueArrow: () => {
// 箭头函数:this 指向外层作用域(这里是全局或 undefined)
return this.value; // 可能不是你期望的
}
};
console.log(obj.getValue()); // 10
console.log(obj.getValueArrow()); // undefined(或报错)
三种方式对比
| 特性 | 函数声明 | 函数表达式 | 箭头函数 |
|---|---|---|---|
| 语法 | function foo() {} |
const foo = function() {} |
const foo = () => {} |
| 提升 | 有(可先调用) | 无 | 无 |
自己的 this |
有 | 有 | 无(继承外层) |
arguments 对象 |
有 | 有 | 无(用剩余参数替代) |
| 适合作为方法 | 是 | 是 | 否(不适合) |
| 适合作为回调 | 可以 | 可以 | 非常适合(简洁) |
函数调用
基本调用
function sayHello() {
console.log('Hello!');
}
sayHello(); // 调用函数
作为方法调用(this 指向)
当函数作为对象的方法调用时,this 指向该对象。
const person = {
name: 'Alice',
greet: function() {
console.log('Hello, I am ' + this.name);
}
};
person.greet(); // Hello, I am Alice(this 指向 person)
使用 call、apply、bind 改变 this
function introduce(age, city) {
console.log(`我是 ${this.name},${age} 岁,来自 ${city}`);
}
const person = { name: 'Alice' };
// call:逐个传入参数
introduce.call(person, 25, 'Beijing');
// 我是 Alice,25 岁,来自 Beijing
// apply:以数组形式传入参数
introduce.apply(person, [30, 'Shanghai']);
// 我是 Alice,30 岁,来自 Shanghai
// bind:返回一个新函数,永久绑定 this
const introAlice = introduce.bind(person);
introAlice(28, 'Guangzhou');
// 我是 Alice,28 岁,来自 Guangzhou
参数
形参和实参
// 形参(定义时的参数)
function add(a, b) {
return a + b;
}
// 实参(调用时传入的参数)
console.log(add(2, 3)); // 2 和 3 是实参
参数数量不匹配
function multiply(a, b) {
console.log('a:', a, 'b:', b);
return a * b;
}
console.log(multiply(2, 3)); // a: 2 b: 3 → 6
console.log(multiply(2)); // a: 2 b: undefined → NaN(b 未传入)
console.log(multiply(2, 3, 4)); // a: 2 b: 3 → 6(多出的参数被忽略)
默认参数(ES6)
// 传统方式
function greet(name) {
name = name || 'Guest';
return 'Hello, ' + name;
}
// ES6 默认参数
function greet(name = 'Guest') {
return 'Hello, ' + name;
}
console.log(greet('Alice')); // Hello, Alice
console.log(greet()); // Hello, Guest
console.log(greet(undefined)); // Hello, Guest(undefined 触发默认值)
console.log(greet(null)); // Hello, null(null 不会触发默认值)
剩余参数(Rest Parameters,ES6)
// 收集所有剩余参数为一个数组
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 与普通参数结合
function multiply(multiplier, ...numbers) {
return numbers.map(n => n * multiplier);
}
console.log(multiply(2, 1, 2, 3)); // [2, 4, 6]
arguments 对象(传统方式)
arguments 是一个类数组对象,包含传入的所有参数。箭头函数中没有 arguments。
// 传统函数
function oldSum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(oldSum(1, 2, 3, 4)); // 10
// 箭头函数中不可用
const arrowSum = () => {
// console.log(arguments); // ReferenceError
};
// 箭头函数应使用剩余参数
const newSum = (...args) => {
return args.reduce((a, b) => a + b, 0);
};
返回值(return)
基本返回值
function add(a, b) {
return a + b; // 返回计算结果
}
let result = add(2, 3);
console.log(result); // 5
没有 return 时返回 undefined
function sayHello() {
console.log('Hello!');
// 没有 return
}
let result = sayHello();
console.log(result); // undefined
返回多个值
// 方式1:返回一个对象
function getPerson() {
return {
name: 'Alice',
age: 25,
city: 'Beijing'
};
}
let person = getPerson();
console.log(person.name); // Alice
console.log(person.age); // 25
// 使用解构赋值
let { name, age } = getPerson();
console.log(name, age); // Alice 25
// 方式2:返回一个数组
function getMinMax(arr) {
return [Math.min(...arr), Math.max(...arr)];
}
let [min, max] = getMinMax([3, 1, 4, 1, 5]);
console.log(min, max); // 1 5
// 方式3:通过参数(不推荐,难以理解)
function getValues(out) {
out.name = 'Bob';
out.age = 30;
}
let obj = {};
getValues(obj);
console.log(obj); // { name: 'Bob', age: 30 }
return 的注意事项
// return 后面换行会导致返回 undefined(ASI 自动分号插入)
function bad() {
return
{ name: 'Alice' }; // 永远不会执行到这里
}
console.log(bad()); // undefined
// 正确写法:保持在同一行或括号紧跟
function good() {
return { name: 'Alice' };
}
console.log(good()); // { name: 'Alice' }
作用域和闭包
函数作用域
函数内部可以访问外部变量,但外部不能访问函数内部变量。
let globalVar = 'global';
function outer() {
let outerVar = 'outer';
function inner() {
let innerVar = 'inner';
console.log(globalVar); // 可以访问:global
console.log(outerVar); // 可以访问:outer
console.log(innerVar); // 可以访问:inner
}
inner();
// console.log(innerVar); // ReferenceError: innerVar is not defined
}
outer();
// console.log(outerVar); // ReferenceError: outerVar is not defined
闭包(Closure)
闭包是指函数可以记住并访问它的词法作用域,即使函数在其词法作用域外执行。
function outer() {
let count = 0; // 这个变量被 inner 函数"记住"了
function inner() {
count++;
console.log(count);
}
return inner; // 返回 inner 函数
}
let counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3
// 每次调用 counter,count 的值都被保留了下来
// 这就是闭包:inner 函数"关闭"了 outer 的变量
闭包的实际应用
1. 数据封装(私有变量)
function createCounter() {
let count = 0; // 私有变量,外部无法直接访问
return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
},
getCount() {
return count;
}
};
}
let counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
// console.log(counter.count); // undefined(无法直接访问)
2. 函数工厂
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
let double = multiplyBy(2);
let triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
3. 防抖(debounce)
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
// 使用
let debouncedSearch = debounce((query) => {
console.log('搜索:', query);
}, 500);
debouncedSearch('hello');
debouncedSearch('hello world'); // 只会在 500ms 后执行最后一次
4. 节流(throttle)
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
高阶函数
高阶函数是指接受函数作为参数或返回函数的函数。
函数作为参数
function greet(name) {
return 'Hello, ' + name;
}
function processUser(name, callback) {
return callback(name);
}
console.log(processUser('Alice', greet)); // Hello, Alice
// 使用箭头函数
console.log(processUser('Bob', (name) => 'Hi, ' + name)); // Hi, Bob
函数作为返回值
function createGreeter(greeting) {
return function(name) {
return greeting + ', ' + name + '!';
};
}
let sayHello = createGreeter('Hello');
let sayHi = createGreeter('Hi');
console.log(sayHello('Alice')); // Hello, Alice!
console.log(sayHi('Bob')); // Hi, Bob!
内置的高阶函数
数组的 map、filter、reduce 等都是高阶函数。
let numbers = [1, 2, 3, 4, 5];
// map:函数作为参数
let doubled = numbers.map(function(n) { return n * 2; });
console.log(doubled); // [2, 4, 6, 8, 10]
// filter:函数作为参数
let evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]
// reduce:函数作为参数,返回累积结果
let sum = numbers.reduce((total, n) => total + n, 0);
console.log(sum); // 15
// sort:函数作为参数,定义排序规则
let unsorted = [3, 1, 4, 1, 5];
unsorted.sort((a, b) => a - b); // 升序
console.log(unsorted); // [1, 1, 3, 4, 5]
递归
递归是指函数调用自身。
基本示例:阶乘
// 递归方式
function factorial(n) {
if (n <= 1) return 1; // 基线条件(停止递归)
return n * factorial(n - 1); // 递归调用
}
console.log(factorial(5)); // 120
// 执行过程:5 * 4 * 3 * 2 * 1 = 120
// 迭代方式(对比)
function factorialIterative(n) {
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}
斐波那契数列
// 递归方式(直观但低效,有重复计算)
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(6)); // 8
// 优化:使用记忆化
function fibonacciMemo(n, memo = {}) {
if (n <= 1) return n;
if (memo[n]) return memo[n];
memo[n] = fibonacciMemo(n - 1, memo) + fibonacciMemo(n - 2, memo);
return memo[n];
}
console.log(fibonacciMemo(10)); // 55
递归的注意事项
// 危险:没有基线条件会导致栈溢出
// function infinite() {
// return infinite(); // RangeError: Maximum call stack size exceeded
// }
// 递归深度过深也会栈溢出
// function deep(n) {
// if (n <= 0) return;
// deep(n - 1);
// }
// deep(100000); // 可能栈溢出
回调函数(Callback)
回调函数是指作为参数传递给另一个函数,并在某个时刻被调用的函数。
同步回调
function processData(data, callback) {
let result = data.toUpperCase();
callback(result); // 同步调用回调
}
processData('hello', (result) => {
console.log('处理结果:', result); // 处理结果: HELLO
});
异步回调
// 模拟异步操作
function fetchData(callback) {
setTimeout(() => {
callback('数据加载完成');
}, 1000);
}
console.log('开始加载...');
fetchData((result) => {
console.log(result); // 1秒后:数据加载完成
});
console.log('继续做其他事情...');
// 输出顺序:
// 开始加载...
// 继续做其他事情...
// 数据加载完成
回调地狱(Callback Hell)
// 多层嵌套的回调(难以维护)
function step1(callback) {
setTimeout(() => callback('步骤1完成'), 1000);
}
function step2(data, callback) {
setTimeout(() => callback(data + ' → 步骤2完成'), 1000);
}
function step3(data, callback) {
setTimeout(() => callback(data + ' → 步骤3完成'), 1000);
}
// 回调地狱
step1((result1) => {
console.log(result1);
step2(result1, (result2) => {
console.log(result2);
step3(result2, (result3) => {
console.log(result3);
// 如果还有更多,会嵌套更深...
});
});
});
// 现代解决方案:使用 Promise 或 async/await
// step1()
// .then(step2)
// .then(step3)
// .then(console.log);
立即执行函数(IIFE)
Immediately Invoked Function Expression,定义后立即执行的函数。
语法
(function() {
// 代码
})();
// 或
(function() {
// 代码
}());
用途:创建私有作用域
// 问题:变量污染全局作用域
var x = 10;
var y = 20;
function helper() { /* ... */ }
// x、y、helper 都在全局作用域中
// 解决:使用 IIFE 创建私有作用域
(function() {
var x = 10;
var y = 20;
function helper() { /* ... */ }
console.log(x + y); // 30
})();
// console.log(x); // ReferenceError: x is not defined
传递参数给 IIFE
(function(global, name) {
console.log('Hello, ' + name);
global.myVar = 'exported'; // 小心地导出到全局
})(window, 'Alice');
现代替代方案
// ES6 模块和块级作用域使得 IIFE 不那么必要了
{
let x = 10;
let y = 20;
console.log(x + y); // 30(使用块级作用域)
}
// x 和 y 在这里不可访问
综合示例
示例 1:函数组合(Pipe)
// 组合多个函数
function pipe(...fns) {
return function(value) {
return fns.reduce((current, fn) => fn(current), value);
};
}
const add10 = (x) => x + 10;
const multiply2 = (x) => x * 2;
const subtract5 = (x) => x - 5;
const process = pipe(add10, multiply2, subtract5);
console.log(process(5)); // (5 + 10) * 2 - 5 = 25
console.log(process(10)); // (10 + 10) * 2 - 5 = 35
示例 2:简易事件发射器
function createEventEmitter() {
let events = {};
return {
on(event, callback) {
if (!events[event]) events[event] = [];
events[event].push(callback);
},
emit(event, data) {
if (events[event]) {
events[event].forEach(callback => callback(data));
}
},
off(event, callback) {
if (events[event]) {
events[event] = events[event].filter(cb => cb !== callback);
}
}
};
}
let emitter = createEventEmitter();
emitter.on('message', (data) => console.log('收到:', data));
emitter.emit('message', 'Hello!'); // 收到: Hello!
示例 3:数据验证器
function createValidator() {
let rules = [];
return {
addRule(rule) {
rules.push(rule);
return this; // 支持链式调用
},
validate(data) {
let errors = [];
for (let rule of rules) {
let result = rule(data);
if (result) errors.push(result);
}
return {
isValid: errors.length === 0,
errors
};
}
};
}
// 定义验证规则
const required = (data) => !data.name ? '姓名不能为空' : null;
const minAge = (data) => data.age < 18 ? '必须年满18岁' : null;
let validator = createValidator()
.addRule(required)
.addRule(minAge);
console.log(validator.validate({ name: 'Alice', age: 16 }));
// { isValid: false, errors: ["必须年满18岁"] }
示例 4:简易缓存函数
function memoize(fn) {
let cache = new Map();
return function(...args) {
let key = JSON.stringify(args);
if (cache.has(key)) {
console.log('从缓存读取:', key);
return cache.get(key);
}
let result = fn.apply(this, args);
cache.set(key, result);
console.log('计算并缓存:', key);
return result;
};
}
// 使用
const slowAdd = (a, b) => {
// 模拟耗时操作
return a + b;
};
const memoizedAdd = memoize(slowAdd);
console.log(memoizedAdd(1, 2)); // 计算并缓存: [1,2] → 3
console.log(memoizedAdd(1, 2)); // 从缓存读取: [1,2] → 3
总结:函数速查表
| 概念 | 语法/说明 |
|---|---|
| 函数声明 | function foo() {} — 有提升 |
| 函数表达式 | const foo = function() {} — 无提升 |
| 箭头函数 | const foo = () => {} — 无 this,简洁 |
| 默认参数 | function foo(x = 0) {} |
| 剩余参数 | function foo(...args) {} |
| 返回值 | return value — 无 return 则返回 undefined |
| 闭包 | 函数"记住"其词法作用域 |
| 高阶函数 | 接受或返回函数的函数 |
| 递归 | 函数调用自身 |
| 回调 | 作为参数传递的函数 |
| IIFE | (function() {})() — 立即执行 |
| this 绑定 | call、apply、bind |