闭包参数和'use'关键字有什么区别?

20

这让我非常困惑,我似乎找不到这个问题的答案。希望能提供一个清晰简单的解释。


我认为你选择了错误的答案。 - Peyman Mohamadpour
2个回答

26
use语句闭包函数创建时捕获变量
常规函数参数在函数调用时捕获
请注意,我在这里区分了变量
function makeAnAdder($leftNum) {
    // Notice that *each time* this makeAnAdder function gets called, we 
    // create and then return a brand new closure function.
    $closureFunc = function($rightNum) use ($leftNum) {
        return $leftNum + $rightNum;
    };

    return $closureFunc;
}

$add5to = makeAnAdder(5);
$add7to = makeAnAdder(7);

echo $add5to(10); // 15
echo $add7to(1); // 8

如果有一种方法可以检查 $add5to 函数的 "源代码",它将会像这样:

function($rightNum) {
    return 5 + $rightNum;
}

我想说的是,use语句允许您维护对变量引用,而不仅仅是该变量在某个时间点上所具有的的副本。为了澄清我的想法:将变量视为一个小盒子,它可以在任何时刻包含单个值,并且该值可以更改。您可以使另一个变量指向该盒子,以便您可以更新盒子中的值或读取其中的当前值。

通常,在函数内创建的局部变量在函数返回后会停止存在。但是,闭包函数可以维护对该变量的引用,并使该局部变量即使在函数返回后也能继续存在 - 这就是闭包函数的真正威力。它让您只需写入少量代码即可模拟类(实例变量)的某些行为。

这里有一个更高级的示例,可能需要深入思考才能理解其行为的细节。

我猜你可以说闭包函数记住了$leftNum的值。

function makeBankAccount() {
    // Each time this makeBankAccount func is called, a new, totally
    // independent local variable named $balance is created.
    $balance = 0;

    // Also, on each call we create 2 new closure functions, $modifyBalance, and $getBalance
    // which will hold a reference to the $balance variable even after makeBankAccount returns.
    $modifyBalance = function($amount) use (&$balance) {
        $balance += $amount;
    };

    $getBalance = function() use (&$balance) {
        return $balance;
    };

    // return both closure functions.
    return ['modifyBalance' => $modifyBalance, 'getBalance' => $getBalance];
}

// Let's prove that bank1 works by adding 5 to the balance by using the first
// function, then using the other function to get the balance
// from the same internal variable.
$bank1 = makeBankAccount();
$bank1['modifyBalance'](5);
echo $bank1['getBalance'](); // 5 - it works.

// Now let's make another bank to prove that it has it's own independent internal $balance variable.
$bank2 = makeBankAccount();
$bank2['modifyBalance'](10);
echo $bank2['getBalance'](); // 10 - as expected. It would have printed 15 if bank2 shared a variable with bank1.

// Let's test bank1 one more time to be sure that bank2 didn't mess with it.
echo $bank1['getBalance'](); // 5 - still 5, as expected.

你可能已经注意到我在这个例子中使用了引用运算符&。如果你还不熟悉它们,只需要知道引用被认为很难理解。尽管如此,我希望这篇文章本身大部分都是有意义的。


你说得很好。我花了一些时间才明白 $rightnum 参数是如何被调用的,但现在我理解了。 - Seralize
是的,有些东西真的很难理解,因为存在太多的间接引用和评估。我会添加一些澄清来帮助理解。 - goat

25
闭包是一个在它自己的环境中评估的函数,该环境有一个或多个绑定变量,当函数被调用时可以访问这些变量。闭包来自函数编程世界,其中存在许多概念。闭包类似于lambda函数,但更智能,因为它们具有与定义闭包所在的外部环境中的变量交互的能力。
使用“use()”关键字允许你将变量从函数外部环境引入到函数内部。要从外部环境导入的变量在闭包函数定义的“use”子句中指定。默认情况下,它们以传值方式传递。所以假设函数没有参数,但你想要它使用一个已经存在的变量。
$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

当您需要创建一个必须用作回调的函数,并且只能定义参数时,这将非常有用。使用use()关键字可以让您除了传递给函数的参数外,还可以使用其他变量。例如在php.net的示例中:http://php.net/manual/en/functions.anonymous.php

public function getTotal($tax)
    {
        $total = 0.00;

        $callback =
            function ($quantity, $product) use ($tax, &$total)
            {
                $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                    strtoupper($product));
                $total += ($pricePerItem * $quantity) * ($tax + 1.0);
            };

        array_walk($this->products, $callback);
        return round($total, 2);
    }

$callback必须只有两个参数,因为array_walk只允许这么多:

通常,funcname采用两个参数。数组参数的值是第一个参数,键/索引是第二个参数。

那我们该怎么办?我们调用use()添加其他不在$callback作用域内但在它被调用的环境作用域内的变量。


对我来说还是不太明白。它跟$closure = function($string) { echo $string; };有什么不同吗? - Seralize
1
@Seralize:抱歉,我更新了我的示例。基本上,PHP中的一些函数允许回调(例如array_walk、array_map等),这些函数有预定义的参数。在这些情况下,您可以调用use()来添加其他变量,否则无法在回调中使用。 - stan
这很有道理。所以 use() 关键字基本上只是一种将变量导入闭包的方法,而不会破坏“参数流”,因为未设置的参数会导致错误? - Seralize
1
@序列化: 是的。它可以让您在不改变参数的情况下为函数添加更多上下文。 - stan
从某种程度上看,这似乎是毫无意义的,但在另一方面,总会有这样的时候,这些小事情可以帮助解决问题。感谢您澄清了这个问题! - Seralize
@Seralize:这就是闭包的全部意义。它与“参数流”或其他无关。你后来给出的是一个总是输出其参数的函数,这并不有趣。通过之前的表达式,我们可以创建一个打印“foo”的函数,或者打印“bar”的函数,或者任何其他函数;而且我们这样做,而不必实际编写不同的函数;每次运行该代码行时,都会创建一个具有新行为的函数(取决于捕获变量的值),就好像你手动为它们编写了单独的函数一样。这就是区别所在。 - newacct

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接