古法编程秘籍(四):函数究竟是什么?把函数最重要的能力一次讲清楚

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 中间件、各种设计模式......很多"高级感",其实都能看到函数思想的影子。

记住一句话

函数从来不只是"一段代码"。

它更像是:软件世界最基础的抽象单位

程序员先用函数解决重复劳动;

再用参数解决变化;

再用回调解决未来;

再用闭包解决记忆;

最后用组合把系统一点点搭起来。

相关推荐
OpenTiny社区1 小时前
一行命令添加 AI 对话入口!TinyRobot 也太省事了~
前端·vue.js·ai编程
sagima_sdu1 小时前
Vue 前端径向渐变背景制作
前端·javascript·vue.js
_Evan_Yao1 小时前
一文搞懂:Git分支管理与团队协作规范——从GitFlow到GitHub Flow,从rebase到merge,打造高效协作流
java·git·后端·github
得物技术2 小时前
用 LLM Agent 重构告警排查流程|得物技术
java·人工智能·后端
Byron__2 小时前
RabbitMQ 面试核心精讲
java·面试·rabbitmq
叶落阁主2 小时前
Vue3 后台管理系统全局菜单搜索实战:Cmd/Ctrl + K、权限菜单与拼音过滤
前端·javascript·vue.js
卷帘依旧2 小时前
setState是同步的还是异步的
前端·面试
卷帘依旧2 小时前
讲一下useEffect和useLayoutEffect
前端·面试
wuhen_n2 小时前
AI Agent 入门:从零实现 LangChain 基础智能体
前端·langchain·ai编程