函数与闭包

上一篇我们学了模块和导入,知道了如何把代码拆分到多个文件里。但光把文件拆开还不够,每个文件内部的代码也需要组织好,这就要用到函数。

这篇我们来学两个核心内容:函数闭包。函数是代码的基本组织单元,闭包是函数的一个强大特性。掌握了这些,你就能写出结构清晰的算法代码了。

函数

函数就是一段可以重复调用的代码块,给它一个名字,需要的时候调用就行。

函数定义与调用

最基本的函数定义方式是用 function 关键字:

这里有两个要注意的地方。第一,JavaScript 的函数参数不需要写类型,直接写参数名就行。第二,如果函数没有 return 语句(或者 return 后面没跟值),返回值就是 undefined

箭头函数

箭头函数是 ES6 引入的一种更简洁的函数写法,用 => 来定义。在刷算法题时箭头函数用得非常多,因为它写起来更简短:

箭头函数的规则总结一下就是:

  • 多个参数或没有参数:必须加括号 (a, b) => ...() => ...
  • 只有一个参数:可以省略括号 x => ...
  • 函数体只有一行且就是返回值:可以省略花括号和 return (a, b) => a + b
  • 函数体有多行:必须加花括号和 return (a, b) => { ... return ...; }

建议初学阶段不要纠结什么时候省略,统一写成 (参数) => { 函数体 } 的完整形式就行,等你熟练了自然会用简写。

默认参数

给参数设置默认值,调用时如果没传这个参数,就自动使用默认值:

默认参数在刷题时经常用到,比如递归函数经常需要一些初始值参数,有了默认参数就不用在外面调用时手动传了。

剩余参数

有时候你不知道调用者会传多少个参数,用 ...args 可以把所有"多余的"参数收集成一个数组:

注意 ...nums 必须放在参数列表的最后面,因为它会"贪婪地"收集剩下所有参数。这个特性在算法题中不常用,但了解一下没坏处。

闭包

什么是闭包

闭包是 JavaScript 中一个非常核心的概念,不过别被这个名字吓到,它的原理很简单:当一个函数访问了它外部作用域的变量时,就形成了闭包

换句话说,函数不仅能使用自己内部定义的变量,还能"记住"并使用定义它时所处环境中的变量。

看到了吗?createGreeter 执行完之后,按理说 greeting 这个局部变量应该"消失"了。但因为返回的箭头函数引用了它,JavaScript 会把它"保留"下来。这就是闭包的本质:内部函数把外部变量"关"在了自己的作用域里,不让它被回收

闭包计数器

闭包最经典的应用就是做计数器,每次调用都能记住上一次的状态:

makeCounter 返回的匿名函数"记住了"外部变量 count,每次调用都会修改并返回它。不同的闭包实例各自维护自己的 count,互不干扰。

闭包在 sort 中的应用

闭包在刷算法题时有一个非常常见的用法:给数组排序时自定义排序规则。这里先给你一个预告,后面"常用数据结构"会详细讲数组的各种操作,到时候会大量用到这个技巧。

sort 方法接受一个"比较函数"作为参数,这个函数能访问外部的 priority 变量。这就是闭包在实战中的典型应用:排序函数"闭合"了外部的数据,用它来决定排序规则

函数也是对象

最后提一个 JavaScript 的重要特点:函数在 JavaScript 中也是一种对象,可以像普通值一样被赋值、传递、存储。

这个特点我们前面其实已经在用了:箭头函数赋值给变量、把函数作为参数传给 sort、从函数中返回另一个函数,这些都是"函数是一等公民"的体现。

这个概念很重要。JavaScript 中到处都在把函数"传来传去":排序时传比较函数、遍历时传回调函数、闭包中返回函数。理解了"函数就是值",这些用法就全通了。

小结

函数让你把代码拆成可复用的小块,闭包让函数可以"记住"外部变量。

关键点回顾:

  • 箭头函数 => 在刷题中用得最多,熟练掌握它的简写规则
  • 默认参数和剩余参数让函数调用更灵活
  • 闭包 = 内部函数引用了外部变量,最常见于 sort 自定义排序
  • 函数在 JavaScript 中是"一等公民",可以赋值、传参、返回

下一篇我们来学 对象与类。对象是 JavaScript 中组织数据的核心方式,而 class 语法让你能更优雅地创建对象。更有意思的是,JavaScript 的类和对象本质上是一回事,背后都是原型链在支撑。