提出php
在匿名函數出現以前,全部的函數都須要先命名才能使用閉包
1函數 2性能 3flex 4this 5spa |
function increment( $value ) code
{ 對象
return $value + 1; ci
}
array_map ( 'increment' , [1, 2, 3]);
|
有的時候函數可能只須要使用一次,這時候使用匿名函數會使得代碼更加簡潔直觀,同時也避免了函數在其餘地方被使用
1 2 3 |
array_map ( function ( $value ){
return $value + 1;
}, [1, 2, 3]);
|
定義和使用
PHP 將閉包和匿名函數視爲同等概念(本文統稱爲匿名函數),本質上都是假裝成函數的對象。
匿名函數的本質是對象,所以跟對象同樣可將匿名函數賦值給某一變量
1 2 3 4 |
$greet = function (string $name ){
echo "hello {$name}" ;
}
$greet ( "jack" )
|
全部的匿名函數都是 Closure 對象的實例
1 |
$greet instanceof Closure
|
對象並無什麼父做用域可言,因此須要使用 use 來手動聲明使用的變量,
1 2 3 4 5 6 7 |
$num = 1;
$func = function () use ( $num ){
$num = $num + 1;
echo $num ;
}
$func ();
echo $num ;
|
若是要讓匿名函數中的變量生效,須要使用引用傳值
1 2 3 4 5 6 7 |
$num = 1;
$func = function () use (& $num ){
$num = $num + 1;
echo $num ;
}
$func ();
echo $num ;
|
從 PHP 5.4 開始,在類裏面使用匿名函數時,匿名函數的 $this 將自動綁定到當前類
1 2 3 4 5 6 7 8 9 10 11 |
class Foo {
public function bar()
{
return function () {
return $this ;
};
}
}
$foo = new Foo();
$obj = $foo ->bar();
$obj ();
|
若是不想讓自動綁定生效,可以使用靜態匿名函數
1 2 3 4 5 6 7 8 9 10 11 |
class Foo {
public function bar()
{
return static function () {
return $this ;
};
}
}
$foo = new Foo();
$obj = $foo ->bar();
$obj ();
|
匿名函數的本質
匿名函數的本質是 Closure 對象,包括瞭如下五個方法
1 2 3 4 5 6 7 |
Closure {
private __construct ( void )
public static bind ( Closure $closure , object $newthis [, mixed $newscope = "static" ] ) : Closure
public bindTo ( object $newthis [, mixed $newscope = "static" ] ) : Closure
public call ( object $newthis [, mixed $... ] ) : mixed
public static fromCallable ( callable $callable ) : Closure
}
|
__construct - 防止匿名函數被實例化
1 2 |
$closure = new \Closure();
|
Closure::bindTo - 複製當前匿名函數對象,綁定指定的 $this 對象和類做用域。通俗的說,就是手動將匿名函數與指定對象綁定,利用這點,能夠爲擴展對象的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Good {
private $price ;
public function __construct(float $price )
{
$this ->price = $price ;
}
}
$addDiscount = function (float $discount = 0.8){
return $this ->price * $discount ;
}
$good = new Good(100);
$count = $addDiscount ->bindTo( $good , Good:: class );
$count ();
$count = $addDiscount ->bindTo( $good );
$count ();
|
Closure::bind - bindTo 方法的靜態版本,有兩種用法:
用法一:實現與 bindTo 方法一樣的效果
1 |
$count = \Closure::bind( $addDiscount , $good , Good:: class );
|
用法二:將匿名函數與類(而不是對象)綁定,記得要將第二個參數設置爲 null
1 2 3 4 5 6 7 8 9 10 11 12 |
class Good {
static $num = 10;
}
$sell = static function () {
return "當前庫存爲" . -- static :: $num ;
};
$sold = \Closure::bind( $sell , null, Good:: class );
$sold ();
$sold ();
|
call - PHP 7 新增的 call 方法能夠實現綁定並調用匿名函數,除了語法更加簡潔外,性能也更高
1 2 3 4 5 |
$addDiscount ->call( $good , 0.5);
$count = $addDiscount ->bindTo( $good , Good:: class , 0.5);
$count ();
|
fromCallable - 將給定的 callable 函數轉化成匿名函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Good {
private $price ;
public function __construct(float $price )
{
$this ->price = $price ;
}
}
function addDiscount(float $discount = 0.8){
return $this ->price * $discount ;
}
$closure = \Closure::fromCallable( 'addDiscount' );
$good = new Good(100);
$count = $closure ->bindTo( $good );
$count = $closure ->bindTo( $good , Good:: class );
$count ();
|
fromCallable 等價於
1 2 |
$reflexion = new ReflectionFunction( 'addDiscount' );
$closure = $reflexion ->getClosure();
|
這裏有一點須要特別注意的是,不管是 fromCallable 轉化成的閉包,仍是使用反射獲得的閉包,在使用 bindTo 時,若是第二個參數指定綁定類,會報錯
1 |
Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()
|