繁体   English   中英

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

[英]In PHP, what is a closure and why does it use the "use" identifier?

我正在检查一些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);
}

作为匿名函数的例子之一。

有人知道这件事吗? 有什么文件吗? 它看起来很邪恶,应该使用它吗?

一个更简单的答案。

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

  1. 闭包是分配给变量的函数,因此您可以传递它
  2. 一个闭包是一个独立的命名空间,通常,你不能访问在这个命名空间之外定义的变量。 来了use关键字:
  3. use允许您访问(使用)闭包内的后续变量。
  4. 使用是早期绑定。 这意味着在定义闭包时复制变量值。 所以修改闭包内的$tax没有外部影响,除非它是一个指针,就像一个对象一样。
  5. 您可以像在&$total情况下一样将变量作为指针传递。 这样,修改$total的值确实有外部影响,原始变量的值发生了变化。
  6. 闭包内部定义的变量也不能从闭包外部访问。
  7. 闭包和函数具有相同的速度。 是的,您可以在所有脚本中使用它们。

正如@Mytskine 指出的那样,最好的深入解释可能是闭包RFC (为此给他点赞。)

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

基本上这意味着您允许匿名函数在其范围之外“捕获”局部变量(在本例中$tax和对$total的引用)并保留它们的值(或在$total的情况下为引用到$total本身)作为匿名函数本身的状态。

function () use () {}就像 PHP 的闭包。

use ,函数无法访问父作用域变量

$s = "hello";
$f = function () {
    echo $s;
};

$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$f(); // hello

use变量的值来自定义函数时,而不是调用时

$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$s = "how are you?";
$f(); // hello

use &引用变量

$s = "hello";
$f = function () use (&$s) {
    echo $s;
};

$s = "how are you?";
$f(); // how are you?

闭包很漂亮! 它们解决了匿名函数带来的许多问题,并使真正优雅的代码成为可能(至少在我们谈论 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(); (或-间隔)。 所以,你传递了一个函数给 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。 命名空间可能更重要,但它们不那么性感

Zupa 很好地解释了“使用”的闭包以及 EarlyBinding 和引用“使用”的变量之间的区别。

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

<?php

$a = 1;
$b = 2;

$closureExampleEarlyBinding = function() use ($a, $b){
    $a++;
    $b++;
    echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."<br />";
    echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."<br />";    
};

echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";  

$closureExampleEarlyBinding();

echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";

/* 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."<br />";
    echo "Inside \$closureExampleReferencing() \$b = ".$b."<br />"; 
};

echo "Before executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "Before executing \$closureExampleReferencing() \$b = ".$b."<br />";   

$closureExampleReferencing();

echo "After executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "After executing \$closureExampleReferencing() \$b = ".$b."<br />";    

/* 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
*/

?>

直到最近几年,PHP 才定义了它的 AST,而 PHP 解释器将解析器与评估部分隔离开来。 在引入闭包期间,PHP 的解析器与求值高度耦合。

因此,当闭包第一次被引入 PHP 时,解释器没有办法知道哪些变量将在闭包中使用,因为它还没有被解析。 所以用户必须通过显式导入来取悦 zend 引擎,做 zend 应该做的功课。

这就是PHP中所谓的简单方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM