跳到主要内容

第八章 函数

函数是JavaScript的主要组成部分。通过函数我们可以更有效地编写程序,也更利于扩展。函数通过一个执行名称包含和对运算分组来帮助我们管理复杂的代码。我们已经通过p5.js中内置的函数ellipse或background了解了如何调用函数。我们甚至还声明了自己的函数,因为p5.js强制我们将代码放在两个函数声明中:setup和draw。如果我们想要创建自己的函数,遵循与这些函数相同的规则创建或声明即可。

创建函数

创建(或声明)函数,我们首先要使用function关键字,然后选择一个可以描述函数功能或目的函数名。参见示例8-1。

示例8-1. 创建一个函数

function functionName() {
// 函数体
}

紧接着函数名的是括号。如果想要创建一个可以接受用户输入的函数,就可以在括号内定义参数来作为占位符变量名来接收用户未来的输入。一会儿我们就会看到如何使用。

然后就是大括号。大括号内的内容可称为函数体。这里我们可以编写构成函数逻辑的代码。我们可以用括号内定义的变量名来使用参数,作为运算的一部分我们需要执行代码体之内的内容。

我们举个简单的例子。看到p5.js里有ellipse函数,但没有circle(正圆)函数了吗?这丝毫不是问题,因为我们可以通过向ellipse函数传入相同的宽和高来创建正圆。为减少使用的参数,假设我们要创建一个circle函数来接收3个参数,x和y是画圆的位置,然后还有一个圆的直径。

示例8-2展示了实现方法。括号内我们写下的变量名,来在函数最终调用时传入。称之为参数是因为它们参数化我们创建的操作的函数。我们在函数内使用这些参数来让用户控制函数的内部运行。

示例8-2. 声明circle函数

function circle(x, y, diameter) {
ellipse(x, y, diameter, diameter);
}

我们可以选择任何参数名,但通常应使用能清晰表达用途的名称。在我们的示例中,x, y和diameter都带有含义。

在定义该函数后,我们使用函数名及传入参数来进行调用。传入函数的值称为函数的参数。注意如果未按要求传入参数函数可能无法正常运行(示例8-3)。

示例8-3. 调用circle函数

circle(width/2, height/2, 100);

如果这些词让你感到困惑请不要太过担心。需要些时间来进行适应。函数的参数可看作用户在使用函数时最终传入的值。调用函数时传入的这些值就称为参数。

有了circle函数,就不需要再使用ellipse函数来画正圆了。我们仅需使用自己的函数来画这些正圆。通过自己实现circle函数,我们知道其实画这些圆的底层使用还是ellipse函数。但函数的好处在于我们无需知晓它的工作原理。我们只需使用函数,而无需了解如何实现。由创建p5.js 的那些高手实现的ellipse函数可能在其内部使用很多技术来画出一个圆,但我们所关心的只是调用它能画出圆形就好了。

本例中的画圆函数并没有为我们提高什么效率。事实上,我们可以向ellipse函数只传递3个参数来画正圆。但函数在构建更为复杂的程序时非常重要。通过在同一个可执行名称下包含或对运算分组来管理复杂性。函数基本上是一个黑盒子,里面包含相应的代码。此外,在函数中通过 var声明的变量在函数外部都不可见。这表示在函数外调用这些变量会产生报错。参见示例8-4。

示例8-4. 变量可见性(作用域)

function setup() {
createCanvas(800, 300);
sayHello();
}

function draw() {
background(220);
}

function sayHello(){
var message = 'Hello World!';
console.log(message);
}

console.log(message); // 本行将抛出报错

第15行的console.log函数将会抛出错误,因为变量message仅在函数sayHello中可见。

Uncaught ReferenceError: message is not defined

函数在无输入(参数)、单个输入或多个输入时都可以使用,也可以返回或不返回结果。下面就说明返回值的意思是什么。

假设我们要创建一个对给定数值相乘的函数,也即计算给定数字的平方值。示例8-5展示了实现的函数。它接收一个数字作为参数,并创建在屏幕上显示该数字的文本。我们使用函数可以在屏幕上显示数字的平方,所以它还是有些用处的。结果请见图8-1。

示例8-5. 创建相乘函数

function setup() {
createCanvas(800, 300);
}

function draw() {
background(1, 75, 100);
squared(10);
}

function squared(num){
fill(237, 34, 93);
textSize(60);
textAlign(CENTER, CENTER);
text(num * num, width/2, height/2);
}

图8-1. 示例8-5的输出]

图8-1. 示例8-5的输出

但如果我们想在另一个计算使用这一些结果数字,就会遇到障碍了。该函数没有返回值,仅仅是在屏幕上进行显示。调用这一函数会影响我们的运行环境,但不会返回值来供更进一步的计算使用。我们所见的部分函数,如ellipse, rectt等等,运行方式类似,也不会返回值来用于计算。执行random函数不会在屏幕上显示,但会返回值来在变量中捕获。

要在函数中返回值我们可以使用return关键字。我们修改squared函数完成这两项:在屏幕上显示结果和返回值(示例8-6)。

示例8-6. 使用return关键字

function setup() {
createCanvas(800, 300);
}

function draw() {
background(1, 75, 100);
var x = squared(10);
console.log(x);
}

function squared(num){
fill(237, 34, 93);
textSize(60);
textAlign(CENTER, CENTER);
var result = num * num;
text(result, width/2, height/2);

// 从函数中返回结果值
return result;
}

现丰,函数可返回值供我们在console.log函数中使用。当程序中出现 return 关键字时,程序会中止函数的执行并返回其后声明的值给函数的调用方。这表示如果在 return 关键字之下还有其它行的话,都不会执行,因为return会中止当前函数的执行。

return关键字只在函数内可用。在函数外使用会产生报错。因为在函数外没有什么可供返回的。

回顾setup 和 draw 函数

既然已经学习了函数的创建,强调声明函数和调用函数的区别就很重要了。注意我们在创建函数后需要进行调用才会执行。例如,示例8-7中代码,我们仅仅是创建或声明了一个函数:

示例8-7. 创建和声明函数

function myFunction() {
}

要使用该函数,我们需要通过调用函数名及在其后使用括号来执行函数,参见示例8-8。

示例8-8. 调用函数

myFunction();

注意使用p5.js时有一件事有些奇怪。我们从没有调用过setup和draw函数,它们还是执行了。这和p5.js的架构有关,p5.js替我们处理了setup和draw函数,因为它们的执行遵循了以下这些简单规则:

  • setup函数在draw函数之前执行
  • setup函数仅执行一次,而draw函数以某一特定速率持续执行

总结

我们在使用p5.js的时候就可以用函数了。它自身的结构要示程序中包含名为setup和draw的两个函数。然后,我们还使用了p5.js中内置的ellipse, rectt等函数。

我们还了解到可以让外部用户输入或不输入来使用函数。也可以选择是否使用return关键字来返回或不返回值。

函数是一种创建可用于重复使用的代码模块。这些函数让我们的通过减少代码的编写量来让程序可维护性更强、更具扩展性。不论何时发现重复使用了代码块,通常都应该创建函数。

练习

创建一个名为grid的函数,包含三个参数:numX和numY在x 轴上创建numX个形状(如矩形)、在y轴上创建numY个形状,还有一个size参数来设置形状的尺寸。

例如:

grid(10, 30, 20); // 创建尺寸为20px的10 x 30个矩形

译者补充,仅供参考:

function setup() {
createCanvas(800, 300);
}

function draw() {
background(1, 75, 100);
grid(30, 10, 20);
}

function grid(numX, numY, size){
fill(237, 34, 93);
for(var i=0; i<numX; i++){
for(var j=0; j< numY; j++){
rect(i*size, j*size, size, size);
}
}
}

创建尺寸为20px的30 x 10个矩形