首頁  >  文章  >  後端開發  >  PHP 核心特性之匿名函數

PHP 核心特性之匿名函數

藏色散人
藏色散人轉載
2019-11-01 13:43:282722瀏覽

提出

在匿名函數出現之前,所有的函數都需要先命名才能使用

function increment($value)
{
    return $value + 1;
}
array_map('increment', [1, 2, 3]);

有的時候函數可能只需要使用一次,這時候使用匿名函數會讓程式碼更簡潔直觀,同時也避免了函數在其他地方被使用

array_map(function($value){
    return $value + 1;
}, [1, 2, 3]);

#定義和使用

PHP 將閉包和匿名函數視為同等概念(本文統稱為匿名函數),本質上都是偽裝成函數的物件。

匿名函數的本質是對象,因此跟對像一樣可將匿名函數賦值給某一變數

$greet = function(string $name){
    echo "hello {$name}";
}
$greet("jack") // hello jack

所有的匿名函數都是Closure 物件的實例

$greet instanceof Closure // true

物件並沒有什麼父作用域可言,所以需要使用use 來手動宣告使用的變量,

$num = 1;
$func = function() use($num){
    $num = $num + 1;
    echo $num;
}
$func();  // 2
echo $num;  // 还是 1

如果要讓匿名函數中的變數生效,則需要使用引用傳值

$num = 1;
$func = function() use(&$num){
    $num = $num + 1;
    echo $num;
}
$func();  // 2
echo $num;  // 2

從PHP 5.4 開始,在類別裡面使用匿名函數時,匿名函數的$this 將自動綁定到目前類別

class Foo {
    public function bar()
    {   
        return function() {
            return $this;
        };
    }
}
$foo = new Foo();
$obj = $foo->bar(); // Closure()
$obj();   // Foo

如果不想讓自動綁定生效,可使用靜態匿名函數

class Foo {
    public function bar()
    {   
        return static function() {
            return $this;
        };
    }
}
$foo = new Foo();
$obj = $foo->bar(); // Closure()
$obj();   // Using $this when not in object context

匿名函數的本質

匿名函數的本質是Closure 對象,包括了以下五個方法

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 - 防止匿名函數被實例化

$closure = new \Closure();
// PHP Error:  Instantiation of 'Closure' is not allowed

Closure::bindTo - 複製目前匿名函數對象,並綁定指定的 $this 物件和類別作用域。通俗的說,就是手動將匿名函數與指定物件綁定,利用這一點,可以為擴充物件的功能。

// 定义商品类
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);
// 将匿名函数绑定到 $good 实例,同时指定作用域为 Good
$count = $addDiscount->bindTo($good, Good::class); 
$count(); // 80
// 将匿名函数绑定到 $good 实例,但是不指定作用域,将无法访问 $good 的私有属性
$count = $addDiscount->bindTo($good); 
$count(); // 报错

Closure::bind - bindTo 方法的靜態版本,有兩種用法:

用法一:實作與bindTo 方法相同的效果

$count = \Closure::bind($addDiscount, $good, Good::class);

用法二:將匿名函數與類別(而非物件)綁定,記得第二個參數要設定為null

// 商品库存为 10
class Good {
    static $num = 10;
}
// 每次销售后返回当前库存
$sell = static function() {
    return"当前库存为". --static::$num ;
};
// 将静态匿名函数绑定到 Good 类中
$sold = \Closure::bind($sell, null, Good::class);
$sold(); // 当前库存为 9
$sold(); // 当前库存为 8

call - PHP 7 新增的 call 方法可以實現綁定並呼叫匿名函數,除了語法更加簡潔外,效能也更高

// call 版本
$addDiscount->call($good, 0.5);  // 绑定并传入参数 0.5,结果为 50
// bindTo 版本
$count = $addDiscount->bindTo($good, Good::class, 0.5); 
$count(); // 50

fromCallable - 將給定的 callable 函數轉換成匿名函數

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 等價於

$reflexion = new ReflectionFunction('addDiscount');
$closure = $reflexion->getClosure();

這裡有一點需要特別注意的是,無論是fromCallable 轉化成的閉包,還是使用反射得到的閉包,在使用bindTo 時,如果第二個參數指定綁定類,會報錯

Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()

以上是PHP 核心特性之匿名函數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除