Hi!这里是 JustHappy 这是专为编程初学者准备的专栏,哈哈,AI 时代更需要我们返璞归真!函数不只是"能调用的一段代码",它最强的能力是:接收参数处理变化、用返回值交结果、用回调把未来交出去、用高阶函数传递规则、用闭包记住状态,再用组合把系统像积木一样搭起来。

上一篇我们说过:函数被发明出来的原因很简单------程序员讨厌重复劳动。
于是大家做了一件特别朴素的事:把一组重复动作起个名字。
比如你写下:
js
login()
你看到的只是一个名字。
但背后可能藏着几百行:校验、请求、拿 token、存状态、跳转......甚至还有各种兜底和重试。
不过,如果函数只有"把动作打包起来"这一招,软件也发展不到今天。
函数真正强的地方在于:它不止能执行动作,还能------接收数据、返回结果、保存状态,甚至传递逻辑。
下面我们把这些能力一次讲清楚。
参数:让同一个函数处理不同数据
最开始的函数可能长这样:
js
function welcome() {
console.log('欢迎张三')
}
很快你就会发现一个现实:用户不只有张三。
你当然不想写 welcomeZhangSan()、welcomeLiSi() 这种东西。
所以参数出现了:
js
function welcome(name) {
console.log(`欢迎你,${name}`)
}
welcome('张三')
welcome('李四')
welcome('王五')
参数的本质一句话:把变化的部分交给外部决定。
所以你可以这么记:
- 函数负责逻辑(怎么做)
- 参数负责数据(对谁做、用什么做)
返回值:把结果交回来
很多人刚学函数的时候,以为函数就是"执行一下动作"。
但真实世界里更多的函数是在"算结果"。
比如最经典的:
js
function sum(a, b) {
return a + b
}
const result = sum(10, 20)
这里发生的事情其实很朴素:
输入 → 处理 → 输出
你会发现绝大多数程序,本质都在反复做这条流水线:数据进来,处理一下,结果出去。
返回值就是那个"把结果拿出来"的出口。
回调:把未来交给别人
接下来你会遇到更真实的场景:请求接口。
js
request('/user')
请求完成之后怎么办?
有人要渲染页面,有人要跳转,有人要缓存,有人要打点上报......场景一多,你就会发现一个尴尬的事:
同样的 request,不同地方"后续处理"完全不一样。
于是程序员又做了一件很自然的事:把后续逻辑也传进去。
js
request('/user', (user) => {
console.log(user)
})
你可以把回调理解成一句话:你先把事情办完,办完之后该怎么处理,我提前写好给你。
所以回调的分工通常是:
- request 负责把数据拿回来
- 回调负责拿到数据后怎么处理
回调的本质:把未来要执行的逻辑提前交出去。
高阶函数:逻辑也可以当数据
看到回调你会突然意识到一件"很离谱但很合理"的事:
数据能传,那逻辑为什么不能传?
于是你会看到这种写法:
js
function execute(fn) {
fn()
}
execute(() => {
console.log('hello')
})
此时:
- 数字可以传
- 字符串可以传
- 函数也可以传
这意味着:逻辑本身变成了数据。
这就是高阶函数(函数接收函数 / 返回函数)最核心的那口气。
你在数组方法里天天用它,只是你可能没意识到:
js
users.filter((user) => user.age > 18)
你传进去的不是数据,而是"筛选规则"。
闭包:让函数拥有记忆
函数默认是"用完就走"的。
执行结束,里面的局部变量就消失。
但项目里你迟早会遇到一类需求:我要记住历史。
比如访问次数、缓存数据、用户状态、购物车数量......
怎么让函数执行结束后还"记得"之前发生过什么?
闭包就是答案。
js
function createCounter() {
let count = 0
return () => ++count
}
const counter = createCounter()
counter() // 1
counter() // 2
counter() // 3
为什么它能一直 +1?
因为 count 没死,它被保留下来了。
闭包解决的问题一句话:让行为携带状态。
这不是 JavaScript 独有的"骚操作",而是软件迟早会遇到的刚需:系统要记住过去,才能做出现在的决定。
柯里化:把配置和执行分开
现实开发里经常出现一种情况:有些东西固定,有些东西变化。
比如域名长期不变,路径经常变:
你不想每次都写:
js
request('https://api.example.com', '/user')
更舒服的写法是:先把环境配置好,再反复执行。
js
const createRequest = (baseUrl) => (path) => fetch(baseUrl + path)
const api = createRequest('https://api.example.com')
api('/user')
api('/order')
api('/product')
你可以把柯里化背后的思想记成六个字:先配置,再执行。
名字听起来很吓人,本质其实很生活。
纯函数:让程序变得可预测
项目一大,bug 一多,程序员就会开始追求三件事:
- 可测试
- 可预测
- 可推导
于是大家会特别喜欢这种函数:
js
function sum(a, b) {
return a + b
}
因为它满足一个很"干净"的特性:
同样输入,永远同样输出。
今天 sum(1, 2) 等于 3,明天也等于 3,不会突然抽风等于 8。
这种函数叫纯函数。
纯函数不是为了装高级,是为了:更好测、更好改、更少意外。
函数组合:像搭积木一样构建系统
最后,当系统越来越大,你会慢慢认同一句老话:一个函数只做一件事,最好维护。
于是你开始把大动作拆成小动作:
js
format()
validate()
save()
再把它们"串起来":
js
const submit = (data) => save(validate(format(data)))
这就是函数组合:把小积木拼成大系统。
你现在看到很多现代框架的设计味道,往往都能追溯到这里:拆小、组合、可替换、可测试。
重新认识函数:它不是一段代码,而是一种能力包
很多新手眼里的函数是语法结构:
js
function add(a, b) {
return a + b
}
但站在软件工程角度看,函数更像一个"最小抽象单位",它至少包含这些能力:
- 封装逻辑(把复杂藏起来)
- 传递数据(参数)
- 返回结果(return)
- 保存状态(闭包/状态)
- 传递逻辑(回调/高阶函数)
- 组合能力(像搭积木)
你后面接触到的 React Hooks、Vue Composition API、Redux 中间件、各种设计模式......很多"高级感",其实都能看到函数思想的影子。
记住一句话
函数从来不只是"一段代码"。
它更像是:软件世界最基础的抽象单位。
程序员先用函数解决重复劳动;
再用参数解决变化;
再用回调解决未来;
再用闭包解决记忆;
最后用组合把系统一点点搭起来。