提出
在匿名函数出现之前,所有的函数都需要先命名才能使用
1
2
3
4
5
|
function increment( $value )
{
return $value + 1;
}
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()
|
明确的学习思路能更高效的学习
点此加入该群学习