在PHP中,什麼是閉包?爲何要使用「 use」標識符?

我正在檢查一些PHP 5.3.0功能,並在網站上遇到了一些看起來頗有趣的代碼: javascript

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);
}

做爲匿名函數的例子之一。 php

有人知道嗎? 有文件嗎? 並且看起來很邪惡,應該使用它嗎? java


#1樓

一個簡單的答案。 程序員

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

  1. 閉包是分配給變量的函數,所以您能夠傳遞它
  2. 閉包是一個單獨的命名空間,一般,您不能訪問此命名空間以外定義的變量。 使用關鍵字:
  3. use容許您訪問(使用)閉包內部的後續變量。
  4. 使用是早期綁定。 這意味着在定義閉包時將複製變量值。 所以,在閉包內部修改$tax不會產生外部影響,除非它像對象同樣是指針。
  5. 您能夠像使用&$total同樣將變量做爲指針傳遞。 這樣,修改$total的值確實具備外部效果,原始變量的值會更改。
  6. 在閉包內部定義的變量也不能從閉包外部訪問。
  7. 閉合和功能具備相同的速度。 是的,您能夠在全部腳本中使用它們。

正如@Mytskine 指出的那樣 ,最好的深刻解釋多是針對閉包RFC 。 (爲此投票給他。) 瀏覽器


#2樓

這就是PHP表示閉包的方式 。 這根本不是邪惡的,實際上它是強大而有用的。 閉包

基本上,這意味着您容許匿名函數在其做用域以外「捕獲」局部變量(在本例中$tax和對$total的引用)並保留其值(或在$total的狀況下,引用爲( $total自己)做爲匿名函數自己內的狀態。 異步


#3樓

封口很漂亮! 它們解決了匿名函數帶來的許多問題,並使真正優雅的代碼成爲可能(至少在咱們談論php時)。 ide

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);
?>

警告:未經測試的代碼(我沒有在atm上安裝php5.3),但它看起來應該像這樣。

有一個缺點:若是您面對閉包的話,許多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和匿名函數/閉包。 命名空間可能更重要, 但它們卻不那麼性感


#4樓

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

?>

#5樓

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?
相關文章
相關標籤/搜索