這篇文章帶給大家的內容是介紹PHP的匿名函數和閉包是什麼?如何建立匿名函數和閉包?有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
概述
閉包和匿名函數在PHP 5.3.0中引入,這兩個特性非常有用,每個PHP開發者都應該掌握。
匿名函數其實就是沒有名稱的函數,匿名函數可以賦值給變量,還能像其他任何PHP函數物件一樣傳遞。不過匿名函數仍然是函數,因此可以調用,還可以傳入參數,適合作為函數或方法的回呼。
閉包是指在創建時封裝周圍狀態的函數,即使閉包所在的環境的不存在了,閉包中封裝的狀態依然存在。
建立匿名函數
建立匿名函數很簡單:
//将匿名函数赋给一个变量,通过变量名+()的形式来调用 $greet = function () { return "Hello World"; }; echo $greet();
結果列印:
Hello World結果列印:
$greet
__invoke()
__invoke
結果列印:
array_map
preg_replace_callback
結果打印>
$numberPlusOne = array_map(function ($number) { return $number += 1; }, [1, 2, 3]); print_r($numberPlusOne);
註:我們之所以能呼叫
變量,是因為這個變量的值是一個閉包,而且閉包對象實現了function incrementNumber ($number) { return $number += 1; } $numberPlusOne = array_map(‘incrementNumber’, [1, 2, 3]); print_r($numberPlusOne);魔術方法,只要變量名後有(),PHP就會尋找並呼叫
方法。
我們通常把匿名函數當做函數或方法的回調使用,事實上,很多PHP函數都會用到匿名函數,例如和,這是使用PHP匿名函數的絕佳時機。記住,閉包和其他值一樣,可以作為參數傳入其他PHP函數:
在匿名函數出現之前,要實現這樣的功能,PHP開發者只能單獨建立具名函數,然後使用名稱來引用這個函數:function makeHelloWorld($name) { $i = 0; return function()use($name, &$i){ echo $name.$i. ' <br>'; $i++; }; } $hello1 = makeHelloWorld("itbsl"); $hello2 = makeHelloWorld("kevin"); $hello1(); $hello1(); $hello1(); $hello2();這樣做把回調的實作和使用場所隔離開了,而且使用閉包實作程式碼更加簡潔。
itbsl0 itbsl1 itbsl2 kevin0
建立閉包
包含自由變數的函數與為所有這些自由變數提供了變數綁定的環境一起,稱為閉包。 bindTo
use
use
列印結果:
從父作用域繼承變數
在PHP中必須手動呼叫閉包物件的方法或使用
關鍵字把父作用域的變數及狀態附加到PHP閉包。而實際應用中,又以使用Route::group(['domain' => '{account}.myapp.com'], function () { Route::get('user/{id}', function ($account, $id) { // }); });關鍵字實現居多。
IlluminateDatabaseEloquent
saveOrFail
實際上,Laravel框架中也大量使用了閉包,最常見的例如路由定義:$options
這裡面的兩個function都是匿名函數。而從父作用域繼承變數的使用場景在Laravel底層原始碼中也是俯拾即是,例如Model.php(Model
)的forceFill
方法:
該方法:
此方法的作用是使用事務將模型資料保存到資料庫,這裡面我們使用匿名函數返回保存狀態,同時使用use關鍵字將父作用域的傳遞給該閉包以便其能夠存取這個資料。
此外,也支援傳遞多個父作用域變數到匿名函數,例如還是在類別中的__invoke
方法:bindTo
__invoke
__invoke()
多個變數以逗號分隔即可。
bindTo
bindTo
bindTo方法
bindTo
$this
我們在前面已經提到,閉包是一個對象,所以我們可以在閉包中使用$this字取得閉包的內部狀態,閉包物件的預設狀態沒什麼用,需要注意的是其中的
class App { protected $routes = []; protected $responseStatus = '200 OK'; protected $responseContentType = 'text/html'; protected $responseBody = 'Laravel学院'; public function addRoute($routePath, $routeCallback) { $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__); } public function dispatch($currentPath) { foreach ($this->routes as $routePath => $callback) { if( $routePath === $currentPath) { $callback(); } } header('HTTP/1.1 ' . $this->responseStatus); header('Content-Type: ' . $this->responseContentType); header('Content-Length: ' . mb_strlen($this->responseBody)); echo $this->responseBody; } }
这里我们需要重点关注addRoute
方法,这个方法的参数分别是一个路由路径和一个路由回调,dispatch
方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。第9行是重点所在,我们将路由回调绑定到了当前的App实例上。这么做能够在回调函数中处理App实例的状态:
$app = new App(); $app->addRoute(‘user/nonfu’, function(){ $this->responseContentType = ‘application/json;charset=utf8’; $this->responseBody = ‘{“name”:”LaravelAcademy"}'; }); $app->dispatch(‘user/nonfu');
在Larval底层也有用到bindTo
方法,详见Illuminate\Support\Traits\Macroable
的__call
方法:闭包和匿名函数(https://laravelacademy.org/post/4341.html)
总结:以上就是本篇文章的全部内容,希望能对大家的学习有所帮助。