zoukankan      html  css  js  c++  java
  • PHP 核心特性之匿名函数

    提出

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

    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()

    明确的学习思路能更高效的学习

    点此加入该群学习

  • 相关阅读:
    asp.net 页面元素分析搜集
    ASP.NET AJAX深入浅出系列UpdatePanel的使用笔记(上)
    用sql语句来管理数据库日志问题
    C# .NET学习网站(转)
    Visual Studio .Net团队开发[转]
    sql 语句大全
    Word中快速操作的10个技巧
    嫁给程序员的好处
    关于手机病毒时代到来的担忧
    自己工作用过的SQL代码(1)
  • 原文地址:https://www.cnblogs.com/heyue0117/p/11812422.html
Copyright © 2011-2022 走看看