JavaScript函数详解
目录
一、函数的基础概念
1. 函数的定义方式
2. 函数的参数处理
3.匿名函数与立即执行函数
4.同名函数与函数提升
二、函数的作用域与闭包
1. 作用域(Scope)
2. 闭包(Closure)
三、高阶函数与函数式编程
1. 高阶函数
2. 纯函数
四、this 绑定与箭头函数
1. this 的指向规则
2. 箭头函数的 this
五、异步函数与生成器
1. 回调函数与异步模式
2. Promise 与 async/await
3. 生成器函数(Generator)
六、函数的高级技巧
1. 柯里化(Currying)
2. 防抖(Debounce)与节流(Throttle)
七、总结
一、函数的基础概念
1. 函数的定义方式
JavaScript 函数可通过多种方式定义,每种方式有不同的使用场景和特性。
方式 | 语法 | 特点 |
---|---|---|
函数声明 | function name() { ... } | 存在函数提升,可先调用后声明 |
函数表达式 | const name = function() { ... } | 无提升,可匿名或命名 |
箭头函数 | const name = () => { ... } | 无 this 、arguments ,不可作为构造函数 |
构造函数 | new Function('a', 'b', 'return a + b') | 动态生成函数,慎用(性能和安全问题) |
示例:
// 函数声明
function add(a, b) { return a + b; }
// 函数表达式
const multiply = function(a, b) { return a * b; };
// 箭头函数
const square = x => x * x;
// 构造函数(极少用)
const subtract = new Function('a', 'b', 'return a - b');
2. 函数的参数处理
JavaScript 函数的参数具有高度灵活性,支持默认值、剩余参数和解构赋值。
特性 | 语法 | 说明 |
---|---|---|
默认参数 | function(a = 1, b = 2) { ... } | ES6+ 支持,参数未传递时使用默认值 |
剩余参数 | function(...args) { ... } | 将剩余参数合并为数组 |
参数解构 | function({ x, y }) { ... } | 从对象或数组中提取值 |
参数数量不匹配:
-
实参多于形参:多余参数被忽略(可通过
arguments
或剩余参数获取)。 -
实参少于形参:缺失的形参值为
undefined
。
示例:
// 默认参数
function greet(name = "Guest") {
console.log(`Hello, ${name}!`);
}
// 剩余参数
function collect(a, ...rest) {
console.log(rest); // 收集剩余实参为数组
}
collect(1, 2, 3); // rest = [2, 3]
// 参数解构
function printCoordinates({ x, y }) {
console.log(`X: ${x}, Y: ${y}`);
}
printCoordinates({ x: 10, y: 20 }); // X: 10, Y: 20
3.匿名函数与立即执行函数
1. 匿名函数
-
定义:无名称的函数,通常作为回调或函数表达式。
const square = function(x) { return x * x; }; // 匿名函数表达式 setTimeout(function() { console.log('Done'); }, 1000); // 回调
2. 立即执行函数(IIFE)
-
作用:创建独立作用域,避免污染全局命名空间。
-
写法:
(function(){ xxx })(); (function(){xxxx}());
(function() { const privateVar = '内部变量'; console.log(privateVar); })(); // 带参数 (function(a, b) { console.log(a + b); })(3, 5); // 输出 8
-
现代替代方案(ES6 块作用域 +
let/const
):{ const privateVar = '内部变量'; console.log(privateVar); }
4.同名函数与函数提升
1. 函数声明提升(Hoisting)
-
后声明的同名函数会覆盖前面的:
console.log(sayHello()); // "Later Hello" function sayHello() { return "Early Hello"; } function sayHello() { return "Later Hello"; } // 覆盖前者
2. 函数表达式无提升
-
按代码顺序执行:
console.log(sayHello()); // ❌ 报错:sayHello 未定义 const sayHello = function() { return "Hello"; };
3. 避免冲突策略
-
模块化:使用 ES6 模块或 IIFE 隔离作用域。
-
对象封装:
const utils = { log: function() { /* ... */ } }; const app = { log: function() { /* ... */ } };
二、函数的作用域与闭包
1. 作用域(Scope)
-
词法作用域(Lexical Scope):函数在定义时确定作用域,而非执行时。
-
块级作用域(Block Scope):
let
/const
声明的变量在{}
内有效。
示例:
function outer() {
const outerVar = "outer";
function inner() {
console.log(outerVar); // 访问外部作用域变量
}
inner();
}
outer(); // 输出 "outer"
- 在能够访问到的情况下先局部,局部没有再找全局。
2. 闭包(Closure)
闭包是函数与其词法环境的组合,允许函数访问定义时的作用域变量,即使外部函数已执行完毕。
经典示例:
function createCounter() {
let count = 0;
return function() {
return ++count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
实际应用:
-
封装私有变量。
-
实现函数工厂(如生成不同配置的函数)。
-
处理异步回调(保留上下文)。
三、高阶函数与函数式编程
1. 高阶函数
接受函数作为参数或返回函数的函数。
示例:
// 接受函数作为参数(如数组方法)
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2); // [2, 4, 6]
// 返回函数
function createMultiplier(factor) {
return function(x) {
return x * factor;
};
}
const triple = createMultiplier(3);
console.log(triple(5)); // 15
2. 纯函数
-
输入相同则输出相同。
-
无副作用(不修改外部状态)。
示例:
// 纯函数
function pureAdd(a, b) {
return a + b;
}
// 非纯函数(修改外部变量)
let total = 0;
function impureAdd(x) {
total += x;
}
四、this
绑定与箭头函数
1. this
的指向规则
-
默认绑定:非严格模式下指向全局对象(浏览器中为
window
),严格模式为undefined
。 -
隐式绑定:函数作为对象方法调用时,
this
指向对象。 -
显式绑定:通过
call()
、apply()
、bind()
指定this
。 -
new
绑定:构造函数中this
指向新实例。
示例:
const obj = {
value: 42,
getValue: function() {
return this.value;
}
};
console.log(obj.getValue()); // 42(隐式绑定)
const unboundGet = obj.getValue;
console.log(unboundGet()); // undefined(默认绑定)
2. 箭头函数的 this
箭头函数没有自己的 this
,继承外层作用域的 this
。
对比示例:
const obj = {
value: 10,
traditional: function() {
setTimeout(function() {
console.log(this.value); // undefined(默认绑定到全局)
}, 100);
},
arrow: function() {
setTimeout(() => {
console.log(this.value); // 10(继承外层 this)
}, 100);
}
};
obj.traditional(); // undefined
obj.arrow(); // 10
五、异步函数与生成器
1. 回调函数与异步模式
传统异步操作依赖回调函数,但易导致“回调地狱”(Callback Hell)。
示例:
fs.readFile('file1.txt', (err, data1) => {
if (err) throw err;
fs.readFile('file2.txt', (err, data2) => {
if (err) throw err;
console.log(data1 + data2);
});
});
2. Promise 与 async/await
ES6+ 引入 Promise 和 async/await
简化异步代码。
示例:
// Promise 链
fetchData()
.then(data => processData(data))
.catch(err => handleError(err));
// async/await
async function fetchAndProcess() {
try {
const data = await fetchData();
const result = await processData(data);
return result;
} catch (err) {
handleError(err);
}
}
3. 生成器函数(Generator)
通过 function*
定义,可暂停和恢复执行,用于复杂异步流程控制。
示例:
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const gen = idGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
六、函数的高级技巧
1. 柯里化(Currying)
将多参数函数转换为单参数函数链式调用。
示例:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return (...args2) => curried(...args, ...args2);
}
};
}
const curriedSum = curry((a, b, c) => a + b + c);
console.log(curriedSum(1)(2)(3)); // 6
2. 防抖(Debounce)与节流(Throttle)
优化高频事件(如滚动、输入)的性能。
防抖示例:
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const debouncedSearch = debounce(searchAPI, 300);
input.addEventListener('input', debouncedSearch);
七、总结
-
优先使用箭头函数:
除非需要动态this
(如对象方法),否则箭头函数更简洁安全。 -
避免过度嵌套:
使用async/await
或 Promise 链替代回调地狱。 -
合理使用闭包:
注意内存泄漏(如无用的变量引用未被释放)。 -
函数单一职责:
一个函数只做一件事,提高可测试性和可维护性。