经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » PHP » 查看文章
PHP 核心特性 - 匿名函数
来源:cnblogs  作者:程序媛的明天  时间:2019/11/13 8:58:45  对本文有异议

 

提出

在匿名函数出现之前,所有的函数都需要先命名才能使用

  1. 1 function increment($value)
  2. 2 {
  3. 3 return $value + 1;
  4. 4 }
  5. 5
  6. 6 array_map('increment', [1, 2, 3]);

 

  1. 有的时候函数可能只需要使用一次,这时候使用匿名函数会使得代码更加简洁直观,同时也避免了函数在其他地方被使用
  1. 1 array_map(function($value){
  2. 2 return $value + 1;
  3. 3 }, [1, 2, 3]);

 

  1.  
 

定义和使用

PHP 将闭包和匿名函数视为同等概念(本文统称为匿名函数),本质上都是伪装成函数的对象

匿名函数的本质是对象,因此跟对象一样可将匿名函数赋值给某一变量

  1. 1 $greet = function(string $name){
  2. 2 echo "hello {$name}";
  3. 3 }
  4. 4
  5. 5 $greet("jack") // hello jack

 

  1.  

所有的匿名函数都是 Closure 对象的实例

  1. $greet instanceof Closure // true

 

  1.  

对象并没有什么父作用域可言,所以需要使用 use 来手动声明使用的变量,

  1. 1 $num = 1;
  2. 2 $func = function() use($num){
  3. 3 $num = $num + 1;
  4. 4 echo $num;
  5. 5 }
  6. 6 $func(); // 2
  7. 7 echo $num; // 还是 1

 

  1.  

如果要让匿名函数中的变量生效,需要使用引用传值

  1. 1 $num = 1;
  2. 2 $func = function() use(&$num){
  3. 3 $num = $num + 1;
  4. 4 echo $num;
  5. 5 }
  6. 6 $func(); // 2
  7. 7 echo $num; // 2

 

  1.  

从 PHP 5.4 开始,在类里面使用匿名函数时,匿名函数的 $this 将自动绑定到当前类

  1. 1 class Foo {
  2. 2 public function bar()
  3. 3 {
  4. 4 return function() {
  5. 5 return $this;
  6. 6 };
  7. 7 }
  8. 8 }
  9. 9
  10. 10 $foo = new Foo();
  11. 11 $obj = $foo->bar(); // Closure()
  12. 12 $obj(); // Foo

 

  1.  

如果不想让自动绑定生效,可使用静态匿名函数

  1. 1 class Foo {
  2. 2 public function bar()
  3. 3 {
  4. 4 return static function() {
  5. 5 return $this;
  6. 6 };
  7. 7 }
  8. 8 }
  9. 9 $foo = new Foo();
  10. 10 $obj = $foo->bar(); // Closure()
  11. 11 $obj(); // Using $this when not in object context

 

  1.  
 

匿名函数的本质

匿名函数的本质是 Closure 对象,包括了以下五个方法

  1. 1 Closure {
  2. 2 private __construct ( void )
  3. 3 public static bind ( Closure $closure , object $newthis [, mixed $newscope = "static" ] ) : Closure
  4. 4 public bindTo ( object $newthis [, mixed $newscope = "static" ] ) : Closure
  5. 5 public call ( object $newthis [, mixed $... ] ) : mixed
  6. 6 public static fromCallable ( callable $callable ) : Closure

 

  1. }

__construct - 防止匿名函数被实例化

  1. $closure = new \Closure();
  2. // PHP Error: Instantiation of 'Closure' is not allowed

 

  1.  

Closure::bindTo - 复制当前匿名函数对象,绑定指定的 $this 对象和类作用域。通俗的说,就是手动将匿名函数与指定对象绑定,利用这点,可以扩展对象的功能。

  1. 1 // 定义商品类
  2. 2 class Good {
  3. 3 private $price;
  4. 4
  5. 5 public function __construct(float $price)
  6. 6 {
  7. 7 $this->price = $price;
  8. 8 }
  9. 9 }
  10. 10
  11. 11 // 定义一个匿名函数,计算商品的促销价
  12. 12 $addDiscount = function(float $discount = 0.8){
  13. 13 return $this->price * $discount;
  14. 14 }
  15. 15
  16. 16 $good = new Good(100);
  17. 17
  18. 18 // 将匿名函数绑定到 $good 实例,同时指定作用域为 Good
  19. 19 $count = $addDiscount->bindTo($good, Good::class);
  20. 20 $count(); // 80
  21. 21
  22. 22 // 将匿名函数绑定到 $good 实例,但是不指定作用域,将无法访问 $good 的私有属性
  23. 23 $count = $addDiscount->bindTo($good);
  24. 24 $count(); // 报错

 

  1.  

Closure::bind - bindTo 方法的静态版本,有两种用法:

用法一:实现与 bindTo 方法同样的效果

  1. $count = \Closure::bind($addDiscount, $good, Good::class);

 

  1.  

用法二:将匿名函数与类(而不是对象)绑定,记得要将第二个参数设置为 null

  1. 1 // 商品库存为 10
  2. 2 class Good {
  3. 3 static $num = 10;
  4. 4 }
  5. 5
  6. 6 // 每次销售后返回当前库存
  7. 7 $sell = static function() {
  8. 8 return"当前库存为". --static::$num ;
  9. 9 };
  10. 10
  11. 11 // 将静态匿名函数绑定到 Good 类中
  12. 12 $sold = \Closure::bind($sell, null, Good::class);
  13. 13
  14. 14 $sold(); // 当前库存为 9
  15. 15 $sold(); // 当前库存为 8

 

  1.  

call - PHP 7 新增的 call 方法可以实现绑定并调用匿名函数,除了语法更加简洁外,性能也更高

  1. 1 // call 版本
  2. 2 $addDiscount->call($good, 0.5); // 绑定并传入参数 0.5,结果为 50
  3. 3
  4. 4 // bindTo 版本
  5. 5 $count = $addDiscount->bindTo($good, Good::class);
  6. 6 $count(0.5); // 50

 

  1.  

fromCallable - 将给定的 callable 函数转化成匿名函数

  1. 1 class Good {
  2. 2 private $price;
  3. 3
  4. 4 public function __construct(float $price)
  5. 5 {
  6. 6 $this->price = $price;
  7. 7 }
  8. 8 }
  9. 9
  10. 10 function addDiscount(float $discount = 0.8){
  11. 11 return $this->price * $discount;
  12. 12 }
  13. 13
  14. 14 $closure = \Closure::fromCallable('addDiscount');
  15. 15 $good = new Good(100);
  16. 16 $count = $closure->bindTo($good);
  17. 17 $count = $closure->bindTo($good, Good::class); // 报错,不能重复绑定作用域
  18. 18 $count(); // 报错,无法访问私有属性

 

  1.  

fromCallable 等价于

  1. 1 $reflexion = new ReflectionFunction('addDiscount');
  2. 2 $closure = $reflexion->getClosure();

 

  1.  

这里有一点需要特别注意的是,无论是 fromCallable 转化成的闭包,还是使用反射得到的闭包,在使用 bindTo时,如果第二个参数指定绑定类,会报错

  1. Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()

 

  1.  

原文链接:http://www.cnblogs.com/a609251438/p/11844920.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号