函数——可复用的代码块

  • 概述:创建 JavaScript 代码块
  • 在 JavaScript 中另一个基本概念是 函数 , 它允许你在一个代码块中存储一段用于处理单任务的代码,然后在任何你需要的时候用一个简短的命令来调用,而不是把相同的代码写很多次。在本文中,我们将探索函数的基本概念,如函数的基本语法、如何定义和调用函数、函数的作用域和参数。

    基本的电脑知识,对 HTML 与 CSS 有基本的了解,及已阅读: JavaScript 第一步 。 了解 Javascript 函数背后的基本概念。

我能在哪找到函数?

在 JavaScript 中,你将发现函数无处不在。事实上,到目前为止,我们一直在使用函数;我们只是还没正式地讨论它们。然而现在是时候了,让我们开始聊聊函数,并探索它们的语法。

几乎任何时候,只要你使用一个带有一对圆括号( () )的 JavaScript 结构,并且你 没有 使用比如 for 循环 while 或 do...while 循环 ,或者 if...else 语句 这样的常见的内置语言结构时,那么你就正在使用函数。

浏览器内置函数

在这套课程中我们已经使用了很多浏览器内置函数。

例如,当我们操作一个字符串的时候:

js
const myText = "我是一个字符串";
const newString = myText.replace("字符串", "香肠");
console.log(newString);
// replace() 字符串函数接受源字符串和目标字符串,
// 将源字符串替换为目标字符串,并返回新形成的字符串

或者当我们操作一个数组的时候:

js
const myArray = ["我", "爱", "巧克力", "青蛙"];
const madeAString = myArray.join(" ");
console.log(madeAString);
// join() 函数接受一个数组,
// 将所有数组元素连接成一个单一的字符串,并返回这个新字符串

或者当我们生成一个随机数时:

js
const myNumber = Math.random();
// random() 函数生成一个随机
// 数字在 0 和 1 之间,并返回该数字

我们都在使用函数

备注: 如果需要,你可以随意将这些代码输入浏览器控制台以便于你熟悉其功能。

JavaScript 语言中有许多内置的函数,它们可以让你无需自己编写所有的代码,就能做很多有用的事情。事实上,许多你调用(专业词语,意指“运行”或“执行”)浏览器内置函数时调用的代码并不能用 JavaScript 来编写——大多数调用浏览器后台的函数的代码,是使用像 C++ 这样更低级的系统语言编写的,而不是像 JavaScript 这样的 web 编程语言。

请记住,这些内置浏览器函数不是核心 JavaScript 语言的一部分——其中部分函数被定义为浏览器 API 的一部分,它建立在默认语言之上,以提供更多的功能(请参阅本课程的早期部分以获得更多的描述)。我们将在以后的模块中更详细地介绍如何使用浏览器 API。

函数与方法

对象的成员 函数 被称为 方法 。你还不必了解 JavaScript 中已构建的对象在更深层次上是如何运作的——你可以等到下一小节,我们会教给你有关对象运作方式的一切以及如何创建它们。在我们继续之前,我们需要澄清一些有关方法和函数概念之间可能存在的混淆——当你在网络上浏览相关信息的时候,你很可能会碰上这两个术语。

到目前为止我们所使用的内置代码同属于这两种形式: 函数 方法 。你可以在 这里 查看内置函数、内置对象以及其相关方法的完整列表。

你在过去的课程中也见到过很多 自定义函数 ——在你的代码中而非浏览器中定义的函数。每当你看到一个后面带有括号的自定义名称,那么你使用的是自定义函数。在我们的 循环文章中 random-canvas-circles.html 示例(参见完整的 源代码 )中,我们包含一个如下所示的自定义 draw() 函数:

js
function draw() {
  ctx.clearRect(0, 0, WIDTH, HEIGHT);
  for (let i = 0; i < 100; i++) {
    ctx.beginPath();
    ctx.fillStyle = "rgb(255 0 0 / 50%)";
    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
    ctx.fill();

该函数在 <canvas> 元素中绘制 100 个随机大小的圆。每次我们想要这样做,我们只需要调用这个函数:

js
draw();

而不是每次我们想要这样做,都需要写出所有的代码。函数可以包含任何你喜欢的代码——甚至可以在函数内调用其他函数。以上示例代码调用了 random() 函数三次,该函数由以下代码定义:

js
function random(number) {
  return Math.floor(Math.random() * number);

我们需要这个函数,因为浏览器的内置 Math.random() 函数只生成一个 0 到 1 之间的随机十进制数。我们想要一个 0 到一个指定数字之间的随机整数。

调用函数

你现在可能很清楚这一点,但以防万一,我们还是提醒一下:要在函数定义之后实际使用它,你必须运行(或调用)它。这可以通过在代码的某个地方包含函数名,然后加上圆括号来实现。

js
function myFunction() {
  alert("你好");
myFunction();
// 调用一次该函数
  

备注: 这种创建函数的形式,也被称为函数声明。它总是被提升的,这样你就能在函数定义之前调用该函数。

函数参数

有些函数在调用它们时需要指定 参数 ——这些值需要放在函数括号内,函数才能正确地完成其工作。

备注: 参数(parameter)有时称为参数(argument)、属性(property)或甚至特性(attribute)。

例如,浏览器的内置 Math.random() 函数不需要任何参数。当被调用时,它总是返回 0 到 1 之间的随机数:

js
const myNumber = Math.random();

浏览器的内置字符串 replace() 函数需要两个参数:在主字符串中查找的子字符串,以及用于替换该字符串的子字符串:

js
const myText = "我是一个字符串";
const newString = myText.replace("字符串", "香肠");
  

备注: 当你需要指定多个参数时,它们以逗号分隔。

可选参数

还应该注意,有时参数不是必需的——你不必指定它们。如果你没有指定某些参数,该函数一般会采用某种默认行为。例如,数组的 join() 函数的参数是可选的:

js
const myArray = ["我", "爱", "巧克力", "青蛙"];
const madeAString = myArray.join(" ");
console.log(madeAString);
// 返回 '我爱巧克力青蛙'
const madeAnotherString = myArray.join();
console.log(madeAnotherString);
// 返回“我,爱,巧克力,青蛙”

如果没有包含参数来指定连接/分隔符,默认情况下会使用逗号。

默认参数

如果你正在编写一个函数,并希望支持可选参数,你可以在参数名称后添加 = ,然后再添加默认值来指定默认值:

js
function hello(name = "克里斯") {
  console.log(`你好,${name}`);
hello("阿里"); // 你好,阿里!
hello(); // 你好,克里斯!

匿名函数和箭头函数

到目前为止,我们创建了如下函数:

js
function myFunction() {
  alert("你好");

但是你也可以创建一个没有名称的函数:

js
(function () {
  alert("你好");
});

这就是所谓的匿名函数,因为它没有名字。当一个函数希望接收另一个函数作为参数时,你经常会看到匿名函数。在这种情况下,函数参数通常作为匿名函数传递。

备注: 这种创建函数的形式也称为函数表达式。与函数声明不同,函数表达式不会被提升

匿名函数示例

例如,你想在用户输入文本框时运行一些代码。为此,你可以调用文本框的 addEventListener() 函数。该函数希望你(至少)传给它两个参数:

  • 要监听的事件名称,本例中为 keydown
  • 事件发生时要运行的函数。
  • 当用户按下某个按键时,浏览器将调用你提供的函数,并传递给它一个包含该事件信息的参数,其中包括用户按下的特定按键:

    js
    function logKey(event) {
      console.log(`You pressed "${event.key}".`);
    textBox.addEventListener("keydown", logKey);
    

    你可以将一个匿名函数传入 addEventListener(),而不是定义一个单独的 logKey() 函数:

    js
    textBox.addEventListener("keydown", function (event) {
      console.log(`You pressed "${event.key}".`);
    });
    

    箭头函数

    如果你传递这样一个匿名函数,你可以使用另一种形式,即 箭头函数 。你可以用 (event) => 来代替 function(event)

    js
    textBox.addEventListener("keydown", (event) => {
      console.log(`You pressed "${event.key}".`);
    });
    

    如果函数只接受一个参数,可以省略参数周围的括号:

    js
    textBox.addEventListener("keydown", event => {
      console.log(`You pressed "${event.key}".`);
    });
    

    最后,如果函数只包含一行 return 语句,也可以省略圆括号和 return 关键字,隐式地返回表达式。在下面的示例中,我们使用 Arraymap() 方法将原始数组中的每个值加倍:

    js
    const originals = [1, 2, 3];
    const doubled = originals.map(item => item * 2);
    console.log(doubled); // [2, 4, 6]
    

    map() 方法依次获取数组中的每一项,并将其传递给给定函数。然后,它将该函数返回的值添加到一个新数组中。

    因此,在上面的例子中,箭头函数 item => item * 2 相当于:

    js
    function doubleItem(item) {
      return item * 2;
    

    你可以使用同样简洁的语法重写 addEventListener 示例。

    js
    textBox.addEventListener("keydown", (event) =>
      console.log(`You pressed "${event.key}".`),
    

    在这种情况下,回调函数隐式返回 console.log() 的值,即 undefined

    我们建议你使用箭头函数,因为它们可以使你的代码更简短、更易读。要了解更多信息,请参阅 JavaScript 指南中的有关部分,以及我们有关箭头函数的参考文档

    备注: 注意:箭头函数和普通函数之间存在一些细微差别。它们超出了本入门指南的范围,并且不太可能在我们在这里讨论的案例中产生影响。要了解更多信息,请参阅箭头函数参考文档

    箭头函数实时示例

    下面是我们上面讨论的“keydown”示例的完整工作示例:

    HTML 文件:

    html
    <input id="textBox" type="text" />
    <div id="output"></div>
    

    JavaScript 文件:

    js
    const textBox = document.querySelector("#textBox");
    const output = document.querySelector("#output");
    textBox.addEventListener("keydown", (event) => {
      output.textContent = `You pressed "${event.key}".`;
    });