初识PHP中的闭包

1 Comment

闭包的概念早已应用在其他某些编程语言中,而且使用的人越来越多,可见大家对函数式编程的喜爱。PHP从5.3后也开始支持闭包,此文就简单介绍一下PHP中闭包的实现。由于本人对闭包的理解和应用水平不高,所以疏漏误解之处请多多包涵并指正 :P
首先来看看这两个概念:Lambda和Closure,其实说它们是“两个”概念有些不合适,Lambda是Closure的基础,两者都是匿名方法,只是使用时略有不同。

引用一下网上的介绍:

Lambda函数(通常所谓的匿名函数)简单来说就是一次性函数,可以在任何时候定义,且经常赋值给一个变量。函数自身和对应变量处于同一个作用域内,当离开变量的作用域后,该函数也将消亡,Lambda不保存任何状态。

闭包(Closure)是指一个可以在自己环境中执行的函数,调用时可以访问已经绑定的变量。它们来源于函数式编程世界,在那里有非常多的应用。闭包和Lambda函数类似,但是闭包还可以和定义时指定的外部变量交互。

我们可以简单地理解为Closure可以访问父级视野(parent scope)定义的变量,而Lambda不行。
下面举个例子来说明,比如我想对某一数组的每个元素追加一个字符后缀,后缀的内容作为参数传递给函数。

//Lambda实现代码:
function appendPostfix($array, $postfix) {
	$anonyFun = function($e, $p) {
		return $e.'_'.$p;
	};
	return array_map($anonyFun, $array, array_fill(0, count($array), $postfix));
}

由于Lambda不能直接调用$postfix变量,所以只好对匿名函数多加个参数,并在调用array_map时将$postfix作为callback参数再次传递一次。

//Closure实现代码:
function appendPostfix($array, $postfix) {
	$anonyFun = function($e) use($postfix) {
		return $e.'_'.$postfix;
	};
	return array_map($anonyFun, $array);
}

通过使用use关键字,将$postfix传入匿名函数(值传递),于是代码简洁了许多。这里只是说明一下闭包的应用,当然可以通过其他方式实现此功能。

那么对于PHP语言来讲使用闭包都有哪些好处:
1. 代码简洁
不用将函数定义在其他地方,提高代码可读性。也不用为避免函数名重复而而额外添加诸如“if(!function_exists(‘xxx’))” 的判断。如果你留意Ruby或Python的代码,会惊讶地发现其简洁的风格和直观的可读性。虽然目前PHP的闭包还达不到那些动态语言的水平,但毕竟是向那个方向所作出的第一步尝试。

2.性能
如果按照以前的办法动态定义一个函数(利用create_function),会有几点不足:由于都是字符串形式,一般IDE无法实现语法高亮;还有就是动态函数是在运行期编译的,而非编译期,所以不能通过opcode来缓存优化性能。

相对的,PHP的闭包有哪些缺点或者说不足:
1. 留意内存泄露
先看一段简单的代码:

function increase() {
	$x = 10;
	return function() use (&$x) {
		return $x++;
	};
}
 
$f = increase();
 
$r = $f();
var_dump($r);
$r = $f();
var_dump($r);

increase方法中的$x是函数局部变量,但是在执行完$f = increase()后并没有被释放掉,因为返回的闭包一直保留着对其的引用。所以在程序输出结果中$x的值会不断递增。这说成是需要注意的地方,不如视为一种编程技巧,我们可以利用这种特性延长函数内部变量的生命周期,相当于在闭包中定义了一个全局的静态变量。

2. 局限
直到5.3.2,PHP的闭包中还不能直接使用$this,需要先将$this赋给一个临时变量,再通过这个临时变量操纵$this,比如:

class Foo
{
    private $x = 3;
    private function f()
    {
        return 15;
    } 
 
    public function getClosureUsingPrivates()
    {
        $self = $this; //注意这行
        return function () use ($self) {
            return $self->x * $self->f();
        };
    }
}

当然,闭包还有很多特性和应用可以挖掘,借鉴下网上总结的常用闭包应用:重构、定制、遍历集合、管理资源、实施策略。
以后可以考虑多使用闭包的形式,以此来学习和体验闭包带来的不同体验。

部分参考文章:
Lambda functions and closures
谈PHP 闭包特性在实际应用中的问题

One Comment (+add yours?)

  1. 羽毛球俱乐部
    Jul 19, 2010 @ 11:29:05

    谢谢啊 你写的很不错哦 记得回踩啊!!

    [Reply]

Leave a Reply