php (匿名函数和闭包)

≡放荡痞女 提交于 2020-04-06 03:51:38

 一、什么是闭包

1、闭包和匿名函数在php5.3.0中两个php新特性,使用的也最多,这两个特性听起来很吓人, 其实很容易理解,这两个特性非常有用,每个php开发者都应该掌握。

2、闭包是指在创建时封装周围状态的函数,即便闭包所在的环境不存在了,闭包中封装的状态依然存在,这个概念很难理解 不过一单掌握了,将会对你的生活带来巨大的变化。

3、匿名函数其实就是没有名称的函数,匿名函数可以赋值给变量,还能像其他任何php对象那样传递,不过匿名函数仍然是 匿名函数,因此可以调用,还可以传入参数,匿名函数特别适合作为函数或方法的回调。 理论上讲,闭包和匿名函数是不同的概念,不过,php将其视作相同的概念,所以,我提到闭包时,指的也是匿名函数, 反之亦然。


4、php闭包和匿名函数使用的句法和普通函数相同,不过别被这一点迷惑了,闭包和匿名函数其实是伪装成函数的对象, 如果审查php闭包和匿名函数,会发现他们是Closure类的实例,闭包和字符串或整数一样,也是一等值类型。  

二、创建一个闭包

$closure = function ($name) {
    return sprintf('Hello %s', $name);
};
 
echo $closure('Yee Jason');
 输出 Hello Yee Jason.

之所以能调用$closure变量,是因为这个变量的值是一个闭包,而且闭包对象实现了 __invoke()魔术方法,只要
变量名后面有 (),php就会查并调用__invoke() 方法。 

我通常把闭包当做函数和方法的回调使用,很多php函数都会用到回调函数,例如 array_map和preg_replace_callback() 是使用匿名函数的绝佳时机,记住,闭包和其他值一样,可以作为参数传入其他php函数。

$numberPlusOne = array_map(function($number) {
    return $number + 1;
}, [1, 2, 3]);
 
print_r($numberPlusOne);
在PHP闭包之前, php开发者无法选择,只能单独创建具名函数,然后引用那个函数,这么做,代码执行的稍微慢一点, 而且把回调的实现和使用场所隔离开了,传统的php代码:

function incrementNumber($number)
{
    return $number + 1;
}
 
$numberPlusOne = array_map('incrementNumber', [1, 2, 3]);
print_r($numberPlusOne);
  以上两个例子输出:Array ( [0] => 2 [1] => 3 [2] => 4 )

三、附加状态
前面演示了如何把匿名函数当成回调使用,下面探讨如何为php闭包附加并封装状态,javascript开发者
可能对php的闭包感到奇怪,因为php闭包不会像真正的javascript闭包那样自动封装应用的状态,在php中,
必须手动调用闭包对象的bindTo()方法或者使用use 关键字,把状态附加到php闭包上。
 
使用 use 关键字附加闭包状态常见的多,因此我们先看这种方式,使用use 关键字把变量附加到闭包上时,
附加的变量会记住附件时付给他的值。
function enclosePerson($name)
{
    return function ($doCommand) use ($name) {
        return sprintf('%s, %s', $name, $doCommand);
    };
}
 
$clay = enclosePerson('Clay');
 
echo $clay('get me sweet tea!');
 以上代码输出:Clay get me sweet tea 

 使用use关键字,把多个参数传入闭包时,需要还是用,号分隔开。

 具名函数enclosePerson() 有个名为$name的参数,这个函数返回一个闭包对象,而且这个闭包对象封装了 $name参数,  即便 返回的闭包对象跳出了 enclosePerson() 函数的作用域,它也会记住$name参数的值,因为$name变量仍在闭包中。

 使用bindTo方法附加闭包的状态

别忘了php 闭包是对象,与任何其他的php对象类似,每个闭包实例都可以使用$this关键字获取闭包的内部状态。
闭包对象的默认状态没什么用,不过有一个 __invoke()魔术方法和bindTo() 方法,仅此而已。
但是bindTo() 方法为闭包增加了一些有趣的潜力,我们可以使用这个方法把Closure对象的内部状态绑定到其他的对象上,
bindTo() 方法的第二个参数很重要,其作用是指定绑定闭包的那个对象所属的php类,因此闭包可以访问绑定闭包的对象中
受保护和私有的成员变量。
 
你会发现,php框架经常使用bindTo()方法把路由URL映射到匿名回调函数上,框架会把匿名函数绑定到应用对象上,
这么做可以在这个匿名函数中使用 $this关键字引用重要的对象。
 
例子:
class APP
{
    protected $routes = array();
    protected $responseStatus = '200 ok';
    protected $responseContentType = 'text/html';
    protected $responseBody = 'hello world';
 
    public function addRoute($routePath, $routeCallback)
    {
        $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
    }
 
    public function dispath($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' . $this->responseBody);
 
        echo $this->responseBody;
    }
}
我们要特别注意addRoute方法,这个方法的参数分别是一个路由路径和路由回调,dispatch() 方法的参数是当前的HTTP请 求的  路径,它会调用匹配的路由回调,我们把路由绑定到当前的App实例上,这么做就能再回调函数中处理App实例的状态 。

$app = new App();
$app->addRoute('/users/josh', function () {
    $this->responseContentType = 'application/json; charset=utf8';
    $this->responseBody = '{"name" : "yee Jason"}';
});
$app->dispatch('/users/josh');

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!