首页 文章

在PHP中,什么是闭包,为什么它使用“use”标识符?

提问于
浏览
352

我正在检查一些 PHP 5.3.0 功能,并在网站上遇到一些看起来很有趣的代码:

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

    $callback =
        /* This line here: */
        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);
}

作为anonymous functions的一个例子 .

有人知道吗?有文件吗?如果它被使用它看起来很邪恶?

5 回答

  • 413

    这就是PHP表达closure的方式 . 这根本不是邪恶的,事实上它非常强大和有用 .

    基本上这意味着你允许匿名函数在其范围之外的"capture"局部变量(在这种情况下, $tax 和对 $total 的引用)并保留它们的值(或者在 $total$total 本身的引用的情况下)为匿名函数本身内的状态 .

  • 35

    一个更简单的答案 .

    function ($quantity) use ($tax, &$total) { .. };

    • 闭包是一个赋给变量的函数,所以你可以传递它

    • 闭包是一个单独的命名空间,通常,您无法访问在此命名空间外定义的变量 . 有 use 关键字:

    • use 允许您访问(使用)闭包内的后续变量 .

    • use 是早期绑定 . 这意味着在定义闭包时变量值是COPIED . 因此,在闭包内部修改 $tax 没有外部效果,除非它是一个指针,就像对象一样 .

    • 您可以将变量作为指针传递,就像 &$total 一样 . 这样,修改 $total 的值有外部效果,原始变量的值会发生变化 .

    • 闭包内定义的变量也无法从闭包外部访问 .

    • 闭包和功能具有相同的速度 . 是的,您可以在脚本上使用它们 .

    至于@Mytskine pointed out可能是最好的深入解释是RFC for closures . (为此赞成他 . )

  • 12

    闭合很漂亮!他们解决了许多匿名函数带来的问题,并且可以使代码变得非常优雅(至少只要我们谈论php) .

    javascript程序员一直使用闭包,有时甚至不知道它,因为绑定变量没有明确定义 - 这就是php中的“use” .

    有比上面更好的现实世界的例子 . 假设您必须按子值对多维数组进行排序,但键会更改 .

    <?php
        function generateComparisonFunctionForKey($key) {
            return function ($left, $right) use ($key) {
                if ($left[$key] == $right[$key])
                    return 0;
                else
                    return ($left[$key] < $right[$key]) ? -1 : 1;
            };
        }
    
        $myArray = array(
            array('name' => 'Alex', 'age' => 70),
            array('name' => 'Enrico', 'age' => 25)
        );
    
        $sortByName = generateComparisonFunctionForKey('name');
        $sortByAge  = generateComparisonFunctionForKey('age');
    
        usort($myArray, $sortByName);
    
        usort($myArray, $sortByAge);
    ?>
    

    警告:未经测试的代码(我没有安装php5.3 atm),但它看起来应该是这样的 .

    有一个缺点:如果你用闭包来对付它们,很多php开发者可能会有点无助 .

    为了更好地理解闭包的好处,我会给你另一个例子 - 这次是在javascript中 . 其中一个问题是范围和浏览器固有的异步性 . 特别是,如果涉及 window.setTimeout(); (或-interval) . 所以,你将一个函数传递给setTimeout,但你不能真正给出任何参数,因为提供参数会执行代码!

    function getFunctionTextInASecond(value) {
        return function () {
            document.getElementsByName('body')[0].innerHTML = value; // "value" is the bound variable!
        }
    }
    
    var textToDisplay = prompt('text to show in a second', 'foo bar');
    
    // this returns a function that sets the bodys innerHTML to the prompted value
    var myFunction = getFunctionTextInASecond(textToDisplay);
    
    window.setTimeout(myFunction, 1000);
    

    myFunction返回一个带有一种预定义参数的函数!

    说实话,自从5.3和匿名函数/闭包以来我更喜欢php . 命名空间可能更重要,但它们的性感要差得多 .

  • 311

    function () use () {} 是PHP的闭包,你必须使用 use 来包含父 function 的变量 .

    <?php
    $message = "hello\n";
    
    
    $example = function () {
        echo $message;
    };
    // Notice: Undefined variable: message
    $example();
    
    
    $example = function () use ($message) {
        echo $message;
    };
    // "hello"
    $example();
    
    
    // Inherited variable's value is from when the function is defined, not when called
    $message = "world\n";
    // "hello"
    $example();
    
    
    // Inherit by-reference
    $message = "hello\n";
    $example = function () use (&$message) {
        echo $message;
    };
    // "hello"
    $example();
    // The changed value in the parent scope is reflected inside the function call
    $message = "world\n";
    // "world"
    $example();
    
    
    // Closures can also accept regular arguments
    $example = function ($arg) use ($message) {
        echo $arg . ' ' . $message;
    };
    // "hello world"
    $example("hello");
    
  • 50

    Zupa用“使用”解释闭包以及EarlyBinding和引用“使用”变量之间的区别做得很好 .

    所以我用一个变量的早期绑定(=复制)做了一个代码示例:

    <?php
    
    $a = 1;
    $b = 2;
    
    $closureExampleEarlyBinding = function() use ($a, $b){
        $a++;
        $b++;
        echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."
    "; echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."
    "; }; echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."
    "; echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."
    "; $closureExampleEarlyBinding(); echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."
    "; echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."
    "; /* this will output: Before executing $closureExampleEarlyBinding() $a = 1 Before executing $closureExampleEarlyBinding() $b = 2 Inside $closureExampleEarlyBinding() $a = 2 Inside $closureExampleEarlyBinding() $b = 3 After executing $closureExampleEarlyBinding() $a = 1 After executing $closureExampleEarlyBinding() $b = 2 */ ?>

    引用变量的示例(注意变量前的'&'字符);

    <?php
    
    $a = 1;
    $b = 2;
    
    $closureExampleReferencing = function() use (&$a, &$b){
        $a++;
        $b++;
        echo "Inside \$closureExampleReferencing() \$a = ".$a."
    "; echo "Inside \$closureExampleReferencing() \$b = ".$b."
    "; }; echo "Before executing \$closureExampleReferencing() \$a = ".$a."
    "; echo "Before executing \$closureExampleReferencing() \$b = ".$b."
    "; $closureExampleReferencing(); echo "After executing \$closureExampleReferencing() \$a = ".$a."
    "; echo "After executing \$closureExampleReferencing() \$b = ".$b."
    "; /* this will output: Before executing $closureExampleReferencing() $a = 1 Before executing $closureExampleReferencing() $b = 2 Inside $closureExampleReferencing() $a = 2 Inside $closureExampleReferencing() $b = 3 After executing $closureExampleReferencing() $a = 2 After executing $closureExampleReferencing() $b = 3 */ ?>

相关问题