匿名函数
匿名函数或 lambda 函数是没有名称的函数。由于它们没有名称,为了能够调用它们,我们需要将它们存储为变量。一开始可能会觉得奇怪,但其实想法很简单。目前,我们并不需要匿名函数,所以只需将代码添加到 init.php
中,然后删除即可:
$addTaxes = function (array &$book, $index, $percentage) {
$book['price'] += round($percentage * $book['price'], 2);
};
php
前面的匿名函数将分配给变量 $addTaxes
。它需要三个参数: $book
(作为引用的数组)、$index
(未使用)和 $percentage
。该函数将税金加到书籍的价格键上,四舍五入到小数点后 2 位(四舍五入是 PHP 本地函数)。不要在意参数 $index
,它在本函数中没有使用,但我们将如何使用它,这将会迫使我们这样做。
您可以将图书列表实例化为一个数组,迭代它们,然后每次都调用该函数。示例如下
$books = [
['title' => '1984', 'price' => 8.15],
['title' => 'Don Quijote', 'price' => 12.00],
['title' => 'Odyssey', 'price' => 3.55]
];
foreach ($books as $index => $book) {
$addTaxes($book, $index, 0.16);
}
var_dump($books);
php
要使用该函数,只需调用 $addTaxes
即可,就好像 $addTaxes
包含了要调用的函数名称一样。函数的其他部分就像普通函数一样工作:接收参数、返回值和作用域。这样定义有什么好处呢?一种可能的应用是将它用作可调用函数。可调用变量是一种变量类型,用于标识 PHP 可以调用的函数。你把这个可调用变量作为参数发送出去,接收它的函数就可以调用它。以 PHP 本地函数 array_walk
为例。它会得到一个数组、一个可调用变量和一些额外的参数。PHP 会遍历数组,每个元素都会调用可调用函数(就像 foreach
循环一样)。因此,你可以用下面的方法替换整个循环:
array_walk($books, $addTaxes, 0.16);
php
array_walk
收到的可调用数组至少需要两个参数:数组当前元素的值和索引,也就是我们之前被迫实现的 $index
参数。它还可以选择接收额外的参数,这些参数就是发送给 array_walk
的额外参数—在本例中,0.16 就是 $percentage
。
事实上,匿名函数并不是 PHP 中唯一的可调用函数。你可以发送普通函数,甚至类方法。让我们看看是如何做到的:
function addTaxes(array &$book, $index, $percentage) {
if (isset($book['price'])) {
$book['price'] += round($percentage * $book['price'], 2);
}
}
class Taxes {
public static function add(array &$book, $index, $percentage)
{
if (isset($book['price'])) {
$book['price'] += round($percentage * $book['price'], 2);
}
}
public function addTaxes(array &$book, $index, $percentage)
{
if (isset($book['price'])) {
$book['price'] += round($percentage * $book['price'], 2);
}
}
}
// using normal function
array_walk($books, 'addTaxes', 0.16);
var_dump($books);
// using static class method
array_walk($books, ['Taxes', 'add'], 0.16);
var_dump($books);
// using class method
array_walk($books, [new Taxes(), 'addTaxes'], 0.16);
var_dump($books);
php
在前面的示例中,你可以看到我们如何将每种情况都用作可调用方法。对于普通方法,只需以字符串形式发送方法名称即可。对于类的静态方法,以 PHP 能理解的方式发送一个数组,其中包含类的名称(包括命名空间的全名,或在前面添加 use
关键字)和方法的名称,两者都是字符串。要使用一个类的普通方法,需要发送一个数组,其中包含该类的实例和字符串形式的方法名。
好了,匿名函数可以用作可调用函数,就像其他函数或方法一样。那么它们有什么特别之处呢?其中之一就是匿名函数是变量,因此它们具有变量的所有优点或缺点。这包括作用域,即函数是在一个作用域内定义的,一旦这个作用域结束,函数就不再可访问。如果你的函数对那段代码来说非常特殊,而且你不可能想在其他地方重复使用它,那么这一点就很有用。此外,由于该函数是无名的,因此不会与任何其他现有函数发生冲突。
使用匿名函数还有另一个好处:从父作用域继承变量。定义匿名函数时,可以使用关键字 use
从定义该函数的作用域中指定某个变量,并在函数中使用它。该变量的值将是它在声明函数时的值,即使后来被更新也是如此。我们来看一个例子:
$percentage = 0.16;
$addTaxes = function (array &$book, $index) use ($percentage) {
if (isset($book['price'])) {
$book['price'] += round($percentage * $book['price'], 2);
}
};
$percentage = 100000;
array_walk($books, $addTaxes);
var_dump($books);
php
前面的示例演示了如何使用关键字 use
。即使我们在定义函数后更新 $percentage
,结果也会显示税率只有 16%。这一点非常有用,因为它可以让你不必在任何需要使用函数 $addTaxes
的地方发送 $percentage
。如果确实需要使用变量的更新值,可以像使用普通函数参数一样,将其声明为引用:
$percentage = 0.16;
$addTaxes = function (array &$book, $index) use (&$percentage) {
if (isset($book['price'])) {
$book['price'] += round($percentage * $book['price'], 2);
}
};
array_walk($books, $addTaxes, 0.16);
var_dump($books);
$percentage = 100000;
array_walk($books, $addTaxes, 0.16);
var_dump($books);
php
在最后这个示例中,第一次 array_walk
使用了原来的值 0.16,因为这仍然是变量的值。但在第二次调用时,$percentage
已经发生了变化,从而影响了匿名函数的结果。