摘要:本教程将介绍 JavaScript 函数的基础知识,以及如何使用函数将代码结构化为更小且可重用的单元。
在脚本的许多地方,我们经常需要执行类似的操作。
在开发应用程序时,您经常需要在多个地方执行相同的操作。为了避免重复编写相同的代码,可以使用函数将其封装并重用。
JavaScript 提供了许多内置函数,如 parseInt()
, parseFloat(), alert(message)、prompt(message, default) 和 confirm(question)。
本教程将指导您如何开发自定义函数。
例如,当访客登录、登出,或在其他场景中,我们可能都需要显示一条漂亮的消息。
函数是程序的主要“构建块”。它们允许我们多次调用相同的代码,而无需重复书写。JavaScript 中的函数是可以定义并在需要时调用的代码片段。它们可以被看作是程序中的子程序。
函数非常有用,因为它们允许程序员只编写一次代码,然后在程序中多次重复使用,从而减少代码重复。同时,函数也使程序员能够创建逻辑上独立的代码段,这些代码段可以与程序中的其他部分区分开来。如果需要更详细的了解函数,可参考C语言的函数介绍。
函数具有以下特点:
-
可以接受零个或多个参数
-
可以在函数调用结束时返回一个指定的值
-
即使不返回值,也可以对程序的其他部分产生副作用

函数声明
要声明一个函数,使用 function
关键字,后跟函数名、参数列表和函数体,如下所示:
例如:
function
关键字写在最前面,然后是函数的名称,接着是括号中的参数列表(用逗号分隔,上面的例子中是空的,稍后我们会看到带参数的例子),最后是函数的代码,也称为“函数体”,包裹在花括号中。
我们可以通过函数名 showMessage()
来调用新函数。
例如:
调用 showMessage()
会执行该函数的代码。在这个例子中,我们会看到这条消息出现两次。
函数名必须是有效的 JavaScript 标识符。按照惯例,函数名采用驼峰命名法,并以动词开头,例如 getData()
、fetchContents()
和 isValid()
。
函数可以接受零个、一个或多个参数。多个参数之间用逗号分隔。
这个例子清楚地展示了函数的主要用途之一:避免代码重复。
如果我们需要更改消息的内容或显示方式,只需修改一个地方:输出消息的那个函数。
在函数体内,您可以编写实现特定操作的代码。例如:
上述 say()
函数将消息输出到控制台。
调用函数
要使用函数,需要调用它。调用函数也称为执行函数。
调用函数的语法如下:
示例:
在此示例中,我们调用了 say()
函数,并传入了字符串 'Hello'
作为参数。
参数与实参
术语“参数”和“实参”经常互换使用,但它们本质上是不同的。
-
参数(Parameter):在声明函数时指定的变量。
-
实参(Argument):在调用函数时传递给函数的实际值。
例如,在 say()
函数中,message
是参数,而 'Hello'
是对应于 message
参数的实参。
返回值
在 JavaScript 中,每个函数默认返回 undefined
,除非您显式指定返回值。
示例:
输出:
要为函数指定返回值,使用 return
语句,后跟表达式或值:
输出:
函数可以根据条件返回不同的值:
此外,您可以使用 return
语句提前退出函数:
arguments 对象
在函数内部,可以访问一个名为 arguments
的对象,它表示传递给函数的所有实参。
arguments
对象类似于数组,但不是数组的实例。您可以使用索引访问各个参数:
函数提升(Hoisting)
在 JavaScript 中,您可以在声明函数之前调用它。这是因为函数声明会被提升到代码的顶部。
示例:
函数提升使得您可以在函数声明之前调用函数,从而使开发流程更加顺畅。
总结
- 使用
function
关键字声明函数。 - 使用
functionName()
调用函数。 - 如果函数没有显式返回值,则默认返回
undefined
。 - 使用
return
语句显式返回值。 arguments
对象是函数内部的类数组对象,表示传递给函数的实参。- 函数提升允许您在声明函数之前调用它。
局部变量
在函数内部声明的变量只能在该函数内部访问。
例如:
外部变量
函数也可以访问外部的变量,例如:
函数可以完全访问外部变量,也可以修改它。
例如:
只有在函数内部没有声明同名的局部变量时,外部变量才会被使用。
如果在函数内部声明了一个同名的变量,它会遮蔽(shadow)外部的变量。例如,在下面的代码中,函数使用的是局部的 userName
,外部的 userName
被忽略了:
全局变量
在任何函数外部声明的变量,比如上面代码中的外部 userName
,称为全局变量。
全局变量可以在任何函数中访问(除非被局部变量遮蔽)。
最好尽量减少使用全局变量。现代代码中全局变量较少或没有。大多数变量都驻留在它们各自的函数中。不过,有时全局变量可以用来存储项目级别的数据。
参数
我们可以通过参数将任意数据传递给函数。
在下面的例子中,函数有两个参数:from
和 text
。
当函数在 (*) 和 (**) 行被调用时,传入的值会被复制到局部变量 from
和 text
中。然后,函数使用这些局部变量。
这是另一个例子:我们有一个变量 from
并将其传递给函数。请注意:函数可以修改 from
,但外部并不会看到这个变化,因为函数总是接收到值的副本:
当一个值作为函数参数传递时,它也被称为实参。
换句话说,澄清这些术语的定义:
-
参数 是在函数声明时括号内列出的变量(这是声明时的术语)。
-
实参 是在调用函数时传递给函数的值(这是调用时的术语)。
我们声明函数时列出其参数,然后在调用时传递实参。
在上面的例子中,可以这样说:“showMessage
函数声明时有两个参数,然后在调用时传入了两个实参:from
和 "Hello"
。”
默认值
如果调用了一个函数,但未提供某个参数,则对应的值将变为 undefined。
例如,上述函数 showMessage(from, text) 可以只用一个参数进行调用:
这不是错误。这样的调用会输出 “Ann: undefined”。由于未传入 text 的值,它变为 undefined。
我们可以在函数声明中使用 = 为参数指定所谓的“默认”(在省略时使用)值:
现在如果未传入 text 参数,它将获得值 “no text given”。
如果参数存在但严格等于 undefined,默认值也会生效,如下所示:
这里的 “no text given” 是一个字符串,但它也可以是更复杂的表达式,且仅在参数缺失时才会被计算并赋值。因此,这也是可行的:
默认参数的求值
在 JavaScript 中,每次调用函数且未提供相应参数时,默认参数都会被求值。
在上面的例子中,如果提供了 text 参数,anotherFunction() 根本不会被调用。
另一方面,每次缺少 text 参数时,它都会被单独调用一次。
默认参数的替代
有时在函数声明之后的某个阶段为参数赋默认值更有意义。
我们可以在函数执行期间通过将参数与 undefined 进行比较来检查是否传入了该参数:
……或者我们可以使用 || 运算符:
现代 JavaScript 引擎支持空值合并运算符 ??,当大多数假值(如 0)应被视为“正常”时,它更合适:
返回值
函数可以将一个值作为结果返回到调用代码中。
最简单的例子是一个将两个值相加的函数:
指令 return 可以出现在函数的任意位置。当执行到它时,函数将停止,并将该值返回给调用代码(在上面的例子中赋值给 result)。
一个函数中可能会有多处 return。例如:
下面的示例在一个函数中使用多个 return
语句来根据条件返回不同的值:
compare()
函数比较两个值。它返回:
- 如果第一个参数大于第二个参数,则返回 -1 。
- 如果第一个参数小于第二个参数,则返回 1。
- 如果第一个参数等于第二个参数,则返回 0。
函数在到达 return
语句时立即停止执行。因此,您可以使用不带值的 return
语句来提前退出函数,如下所示:
例如:
在上面的代码中,如果 checkAge(age) 返回 false,那么 showMovie 将不会继续执行 alert。
JavaScript 函数、变量、和常量的命名规范
1. 命名规范
ECMAScript 规范中标识符采用驼峰大小写格式,驼峰命名法由小(大)写字母开始,后续每个单词首字母都大写。根据首字母是否大写,分为两种方式:
- Pascal Case 大驼峰式命名法:首字母大写。eg:StudentInfo、UserInfo、ProductInfo
- Camel Case 小驼峰式命名法:首字母小写。eg:studentInfo、userInfo、productInfo
标识符,则包括变量、函数名、类名、属性名和函数或类的参数,每个命名方法又略有不同,下面详细解释一下:
2. 函数的命名
函数是执行某些操作的行为。因此,它们的名称通常是动词。名称应简洁、尽可能准确,并描述函数的作用,以便阅读代码的人能大致了解该函数的功能。
一个广泛采用的做法是用动词前缀来命名函数,这些前缀模糊地描述了动作。团队内部应就这些前缀的含义达成一致。
命名方法:小驼峰式命名法。
命名规范:前缀应当为动词。
命名建议:可使用常见动词约定
动词 | 含义 | 返回值 |
---|---|---|
can | 判断是否可执行某个动作(权限) | 函数返回一个布尔值。true:可执行;false:不可执行 |
has | 判断是否含有某个值 | 函数返回一个布尔值。true:含有此值;false:不含有此值 |
is | 判断是否为某个值 | 函数返回一个布尔值。true:为某个值;false:不为某个值 |
get | 获取某个值 | 函数返回一个非布尔值 |
set | 设置某个值 | 无返回值、返回是否设置成功或者返回链式对象 |
load | 加载某些数据 | 无返回值或者返回是否加载完成的结果 |
以下是一些常见前缀及其含义:
-
“get…” – 返回一个值,
-
“calc…” – 计算某些内容,
-
“create…” – 创建某些内容,
-
“check…” – 检查某些内容并返回布尔值,等等。
这类名称的示例:
有了这些前缀,一眼就能从函数名中了解它的作用以及返回值的类型。
函数命名注意事项
一个函数 —— 一个动作
一个函数应当只做其名称所暗示的事情,不多也不少。
两个独立的动作通常应该写成两个函数,即使它们经常一起被调用(这种情况下我们可以写一个第三个函数来调用这两个)。
以下是一些违反该原则的例子:
-
getAge
—— 如果它弹出一个显示年龄的警告框,那就不合适(它应该只是获取年龄)。 -
createForm
—— 如果它修改了文档并将表单添加到其中,那就不妥(它应该只是创建并返回表单)。 -
checkPermission
—— 如果它显示了“允许访问/拒绝访问”的消息,那就不好(它应该只执行检查并返回结果)。
这些例子基于前缀的一般含义。你和你的团队可以就其他含义达成一致,但通常不会有太大差别。无论如何,你应当清楚了解每个前缀的意义,知道带有该前缀的函数应该做什么、不能做什么。所有使用相同前缀的函数都应遵循同一规则,并在团队中达成共识。
超短的函数名称
那些非常常用的函数有时会使用超短的名称。
例如,jQuery 框架定义了一个函数,名称是 $。Lodash 库的核心函数名为 _。
这些是例外。通常情况下,函数名称应简洁且具有描述性。
函数 == 注释
函数应该简短并且只做一件事。如果这件事很大,也许值得将函数拆分成几个更小的函数。有时候遵循这个规则可能并不容易,但它绝对是有益的。
一个单独的函数不仅更容易测试和调试——它的存在本身就是一个很好的注释!
例如,比较下面的两个 showPrimes(n) 函数。每个函数都会输出小于等于 n 的质数。
下面第一段CODE使用了标签:
第二段COD使用了一个额外的函数 isPrime(n) 来测试是否为质数:
第二段code更容易理解,不是吗?我们看到的是一个动作的名称(isPrime),而不是一段代码。有时人们将这样的代码称为自描述(self-describing)代码。
因此,即使我们不打算重复使用它们,函数仍然可以被创建。它们帮助结构化代码并提高可读性。
3. 变量的命名
命名方法:小驼峰式命名法。
命名规范:前缀应当是名词。(函数的名字前缀为动词,以此区分变量和函数)
命名建议:尽量在变量名字中体现所属类型,如:length、count等表示数字类型;而包含name、title表示为字符串类型。
4. 常量
命名方法:名称全部大写。
命名规范:使用大写字母和下划线来组合命名,下划线用以分割单词。