JavaScript 函数(手把手讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
在 JavaScript 开发中,函数是构建程序逻辑的核心工具。无论是简单的数据计算,还是复杂的异步操作,函数都如同程序中的“积木块”,通过灵活组合实现多样化功能。对于编程初学者而言,理解函数的定义、参数传递、作用域等基础概念至关重要;而中级开发者则需要掌握闭包、高阶函数等进阶特性,以提升代码的复用性和可维护性。本文将从零开始,逐步解析 JavaScript 函数的原理与实践,帮助读者建立系统化的知识框架。
函数的基础定义与调用
函数是什么?
函数可以类比为一个“自动化流程”。例如,厨房中的厨师按照菜谱(函数)制作菜品:输入食材(参数),经过加工(函数体),最终产出成品(返回值)。在 JavaScript 中,函数是封装特定任务的代码块,通过名称调用即可执行。
定义函数的两种方式
-
函数声明(Function Declaration):
function calculateArea(radius) { return Math.PI * radius * radius; }
这种方式通过
function
关键字声明,函数名calculateArea
可以在声明前的任何位置调用(得益于 JavaScript 的“函数提升”特性)。 -
函数表达式(Function Expression):
const add = function(a, b) { return a + b; };
此时函数被赋值给变量
add
,调用方式为add(2, 3)
。函数表达式不被提升,需先定义后使用。
函数调用与参数传递
函数调用时,参数的传递遵循“按值传递”原则。例如:
function greet(name) {
console.log(`Hello, ${name}!`);
}
greet("Alice"); // 输出 "Hello, Alice!"
当参数是对象或数组时,传递的是引用地址而非原始值。例如:
function modifyObject(obj) {
obj.name = "Bob";
}
const person = { name: "Alice" };
modifyObject(person);
console.log(person.name); // 输出 "Bob"
参数与返回值的深度解析
默认参数与可选参数
在 ES6 之前,开发者常通过 if
判断或 arguments
对象处理可选参数。ES6 引入了默认参数,使代码更简洁:
function makeCoffee(beans = 10, water = 200) {
return `使用 ${beans}g 咖啡豆和 ${water}ml 水冲泡`;
}
console.log(makeCoffee()); // 使用 10g 咖啡豆和 200ml 水冲泡
灵活传递参数:arguments
与 rest 参数
-
arguments
对象:
在函数内部,arguments
是一个类数组对象,可访问所有传入参数:function sum() { return Array.from(arguments).reduce((acc, num) => acc + num, 0); } console.log(sum(1, 2, 3)); // 输出 6
但
arguments
不支持数组方法(如map
),需转换为真实数组。 -
rest 参数
(ES6):
使用...
语法替代arguments
,直接获取参数的数组形式:function multiply(...numbers) { return numbers.reduce((acc, num) => acc * num, 1); } console.log(multiply(2, 3, 4)); // 输出 24
返回值的多态性
函数可以返回任意类型,甚至函数本身。例如:
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
console.log(double(5)); // 输出 10
此例中,函数 createMultiplier
返回了一个匿名函数,实现了“函数工厂”的功能。
函数的作用域与闭包
作用域的层级结构
JavaScript 的作用域分为全局作用域、函数作用域和块级作用域(ES6 引入)。函数内部可以直接访问外部变量,但外部无法直接访问函数内部变量。例如:
let globalVar = "全局变量";
function outerFunction() {
let outerVar = "外部函数变量";
function innerFunction() {
let innerVar = "内部函数变量";
console.log(globalVar, outerVar); // 可访问
}
// console.log(innerVar); // 报错,无法访问内部变量
}
闭包(Closure)的定义与应用
闭包是函数与对其周围状态(即词法环境)的引用捆绑。例如:
function counter() {
let count = 0;
return function() {
count++;
return count;
};
}
const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2
此处,increment
函数引用了外部 counter
的 count
变量,形成了闭包。闭包常用于封装私有变量或实现计数器、回调函数等场景。
高阶函数与函数式编程
高阶函数的定义
高阶函数是指满足以下任一条件的函数:
- 接收其他函数作为参数
- 返回一个函数作为结果
例如,Array.prototype.map
是一个典型高阶函数:
const numbers = [1, 2, 3];
const squares = numbers.map(num => num * num); // [1, 4, 9]
函数柯里化(Currying)
柯里化是将多参数函数转换为单参数函数链的过程。例如:
function add(a) {
return function(b) {
return a + b;
};
}
const add5 = add(5);
console.log(add5(3)); // 8
此方法可预设参数,提升代码灵活性。
箭头函数与 this 绑定
箭头函数的语法与特性
ES6 引入的箭头函数简化了函数书写:
// 传统函数
function square(x) { return x * x; }
// 箭头函数
const square = x => x * x;
箭头函数有以下特点:
- 没有
arguments
对象,但可通过...args
获取参数 - 没有独立的
this
绑定,继承外层函数的this
- 不能用作构造函数(无
new
关键字支持)
this 绑定的对比
传统函数的 this
取决于调用方式,而箭头函数的 this
始终指向定义时的外层作用域。例如:
const obj = {
value: 10,
传统函数:
method: function() {
setTimeout(function() {
console.log(this.value); // undefined,因函数内部 this 指向全局
}, 100);
},
箭头函数:
method2: function() {
setTimeout(() => {
console.log(this.value); // 10,继承外层 this
}, 100);
}
};
函数防抖与节流
场景需求
在处理高频事件(如窗口调整、输入框输入)时,频繁触发函数可能导致性能问题。此时需要使用防抖(Debounce)和节流(Throttle)。
函数防抖
防抖的核心是“在事件停止触发后,延迟执行函数”。例如:
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
// 应用:
window.addEventListener("resize", debounce(handleResize, 300));
函数节流
节流则是“在指定时间间隔内,确保函数最多执行一次”。例如:
function throttle(func, delay) {
let canRun = true;
return function(...args) {
if (!canRun) return;
canRun = false;
setTimeout(() => {
func.apply(this, args);
canRun = true;
}, delay);
};
}
实战案例:构建模块化计算器
以下示例通过函数组合实现一个简单计算器:
// 基础操作函数
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;
// 组合函数
function calculate(a, operator, b) {
const ops = { add, subtract, multiply };
return ops[operator](a, b);
}
// 使用防抖处理输入
const handleInput = debounce((input) => {
const [a, op, b] = input.split(" ");
console.log(calculate(Number(a), op, Number(b)));
}, 500);
// 模拟用户输入
handleInput("5 add 3"); // 输出 8
结论
JavaScript 函数不仅是代码逻辑的容器,更是实现模块化、复用性和高性能的关键工具。从基础定义到闭包、高阶函数,再到防抖节流等进阶技巧,掌握这些知识点能显著提升开发效率与代码质量。对于初学者,建议从函数的参数传递、作用域入手,逐步理解闭包等抽象概念;中级开发者则可深入探索函数式编程、设计模式等场景,将函数的威力发挥到极致。
JavaScript 函数的灵活性与强大功能,正驱动着现代 Web 开发的无限可能。希望本文能成为你探索这一领域的坚实起点。