提出
在匿名函数出现之前,所有的函数都需要先命名才能使用
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" ) // hello jack
|
所有的匿名函数都是 Closure 对象的实例
1
|
$greet instanceof Closure // true
|
对象并没有什么父作用域可言,所以需要使用 use 来手动声明使用的变量,
1
2
3
4
5
6
7
|
$num = 1;
$func = function () use ( $num ){
$num = $num + 1;
echo $num ;
}
$func (); // 2
echo $num ; // 还是 1
|
如果要让匿名函数中的变量生效,需要使用引用传值
1
2
3
4
5
6
7
|
$num = 1;
$func = function () use (& $num ){
$num = $num + 1;
echo $num ;
}
$func (); // 2
echo $num ; // 2
|
从 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(); // Closure()
$obj (); // Foo
|
如果不想让自动绑定生效,可使用静态匿名函数
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(); // Closure()
$obj (); // Using $this when not in object context
|
匿名函数的本质
匿名函数的本质是 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();
// PHP Error: Instantiation of 'Closure' is not allowed
|
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);
// 将匿名函数绑定到 $good 实例,同时指定作用域为 Good
$count = $addDiscount ->bindTo( $good , Good:: class );
$count (); // 80
// 将匿名函数绑定到 $good 实例,但是不指定作用域,将无法访问 $good 的私有属性
$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
|
// 商品库存为 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 方法可以实现绑定并调用匿名函数,除了语法更加简洁外,性能也更高
1
2
3
4
5
|
// call 版本
$addDiscount ->call( $good , 0.5); // 绑定并传入参数 0.5,结果为 50
// bindTo 版本
$count = $addDiscount ->bindTo( $good , Good:: class , 0.5);
$count (); // 50
|
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()
|
明确的学习思路能更高效的学习
点此加入该群学习
最新文章
- Shell编程和Vim操作
- 【2016-10-28】【坚持学习】【Day15】【Oracle】【变量 定义 使用】
- Android SDK Manager无法更新的解决
- Node.js模块
- 一款查看mysql QPS的脚本
- [CareerCup] 3.6 Sort Stack 栈排序
- Resumable Media Uploads in the Google Data Protocol
- 阿里云服务器mysql修改编码问题
- Call C# code from C++
- mvc actionresult 判断是否回发?
- IPhone开发“此证书是由未知颁发机构签名”解决办法
- 【CJOJ2440】大话西游(树链剖分)
- [转]解决scrapy下载图片时相对路径转绝对路径的问题
- [APIO2012]
- java虚拟机的堆内存配置
- 3.1Python数据处理篇之Numpy系列(一)---ndarray对象的属性与numpy的数据类型
- QTimer的一些注意事项和探索
- idea不识别yml配置文件,怎么办?
- 一种多线程写日志文件的解决方案 c#源代码演示
- Oracle EBS PO退货失败
热门文章
- electron调用c#动态库
- Shiro learning - 认证流程(3)
- 算法随笔-二叉树遍历的N种姿势
- Linux下zkui的安装
- Ubuntu 14.04 sudo免密码的方法| sudo不需要密码
- Nginx在Window上简单的使用
- Android dos操作
- vue 请求图片方法
- 痞子衡嵌入式:飞思卡尔i.MX RTyyyy系列MCU硬件那些事(2.2)- 在串行NOR Flash XIP调试原理
- git push 报src refspec xxx does not match any的错误