匿名函数学习

300 阅读5分钟

学习背景

在看 laravel 源码的时候,有很多的匿名函数。对于小白,这样看的我头疼。不过,多看几遍之后,慢慢也就看懂了,为了加深印象,就想深入学习下匿名函数。

参考文章

链接:www.cnblogs.com/echojson/p/…

PHP Closure(闭包)类详解

Closure

闭包是为了函数的复用,PHP 里面闭包函数是为了复用函数而设计的语言特性,如果在闭包函数里面访问指定域的变量,使用 use 关键字来实现。PHP 具有面向函数的编程特性,也是面向对象编程语言。PHP 会自动把闭包函数转换成内置类 Closure 的对象实例,依赖 Closure 的对象实例又给闭包函数添加更多能力。闭包不能被实例化(私有构造函数),也不能被继承。可以通过反射来判读闭包实例是否能被实例和继承!

$say = function () {
    echo "Say";
};

var_dump($say);
//下面是打印结果
//PHP会自动把闭包函数转换成内置类 Closure 的对象实例
object(Closure)#1 (0) {

}

匿名函数

$say = function() {
    echo "Say";
}

// 1:匿名函数的调用
$say();

test(Closore $callback) {
    return $callback();
}

// 2:第二种调用
test($say);

实现闭包

将匿名函数在普通函数中当做参数传入,也可以在函数中被返回。 连接闭包和外界变量的关键字:USE

$helloWorld = function () {
    $hello =  "Hello";
    // use 引用的变量也只不过是一个变量的副本 CLONE 而已(非完全引用变量本身),因此如果要对变量有修改就必须在变量之前加上一个 & 符号。
    $worlds = function () use(&$hello) {
       $hello .= " World";
    };
    $worlds();
    echo $hello;
};

$helloWorld();
var_dump($helloWorld);

类里面的匿名函数

class A {
    public static function testA() {
        return function ($i) {
            return $i+100;
        };
    }
}

function B(Closure $callback) {
    return $callback(200);
}
// testA返回的就是一个匿名的函数
$a = B(A::testA());
print_r($a);

匿名函数的绑定

匿名函数的访问不在是匿名函数范围,而是一个类的访问范围,那么我们就将”一个匿名函数绑定到一个类中”

Closure {
    __construct ( void )
    public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static' ])
    public Closure bindTo (object $newthis [, mixed $newscope = 'static' ])
}

参数说明

closure:需要绑定的匿名函数 newthis:需要绑定到匿名函数的对象,或者NULL创建未绑定的闭包 newscope:想要绑定给闭包的类作用域,或者 ‘static’ 标识 不改变。如果传入一个对象,则使用这个对象的类别名。类作用域来决定在闭包中 $this 对象的私有、保护方法的可见性。可以传入类名或者类的实例,默认值是 ‘static’ ,表示不改变。 返回值:返回一个新的 Closure 对象,或者在失败时返回 false。

Closure::bind 和 Closure::bindTo的静态版本

深入理解bind参数,要理解bind参数,先要理解类的属性的访问权限 public、protect(子类可以访问)、private(子类可以访问,类外不可以访问)。 public:公有类型,在子类中可以通过 self::var调用public方法或是属性,parent::method调用父类方法,在实例中可能够用var调用 public 方法或是属性,parent::method 调用父类方法,在实例中可能够用obj->var 来调用 public 类型的方法或是属性。 protect:受保护类型在子类中可以通过 self::var调用protect方法或属性,parent::method调用父类,在实例中不能通过var 调用protect方法或属性,parent::method 调用父类,在实例中不能通过 obj->var来调用protect类型的方法和属性。 private:私有类型的属性或是方法只能在该类中使用,在该类实例、子类、子类中的实例都能调用私有属性和方法。

不能通过 this访问静态变量,静态方法里面也不能用this 访问静态变量,静态方法里面也不能用 this 。原因:静态属性属于类本身而不属于类的任何实例。静态属性可以被看作是存储在类中的全局变量,可以在任何地方通过类访问它们。在类外不能通过 类名::私有静态变量,只能在类里通过 self或者 static 访问私有静态变量。

第一个参数:需要绑定的匿名函数。 第二个参数:关闭bind的第二参数为 object 还是 null,取决于第一个参数闭包中是否用发哦 ·this的上下文。(绑定的对喜爱那个决定了函数中this·的上下文。(绑定的对喜爱那个决定了函数中 this 的取值)若闭包中用到 $this 则第二个参数不能为 null,只能为 object实例对象;若闭包用到静态访问(::),第二个参数就可以为null,也可以为 object。 第三个参数:作用域,是控制闭包的作用域,如果闭包访问的private属性,就需要第三个参数提升闭包的访问权限,若闭包访问的public属性,第三个参数可以不用。只不要改变访问权限时才需要,传对象或者类名都行。

<?php

class A {
    private $name = '猫王';
    protected $age = 30;
    private static $weight = '70kg';
    public $address = '中国';
    public static $height = '180cm';
}

$obj = new A();
// 报错 私有属性,不能实例话访问
//$obj->name;
// 共有属性,不能实例化访问
//$obj->age;
// 私有静态方法,不能类名访问
//A::$weight;
$obj->address;
A::$height;

$fun = function () {
    // 单独的代码是不能运行 $this 的,程序根本不知道我要调用哪个对象,即使是知道是哪个对象是否拥有这个属性,
    // 没有这个属性也同样会报错
    return $this->name;
};
// 报错 Fatal error: Uncaught Error: Using $this when not in object context
//echo $fun();

$fun = function () {
    return A::$height;
};
//echo $fun();

$fun = function () {
    return $this->name;
};
// 第一种情况:bind 的第二个参数为 null
// 报错,因为bind的第二个参数为 null,所以 $fun 函数中的 $this 不知道是哪个 $this,因为会报错。
// Fatal error: Uncaught Error: Using $this when not in object context

// 第二种情况:
// 第二个参数指定的类,第三个参数为 null,同样会报错。
// 报错原因是 A类中 name属性数私有方法,不能访问。
//  Uncaught Error: Cannot access private property A::$name

// 注意 当匿名函数中用到 $this ,bind的第二个参数一定是 为该类,不然会报错、
$name = Closure::bind($fun,new A(),new A());
//echo $name();

$fun = function () {
    return self::$weight;
};
$wight = Closure::bind($fun,null,A::class);
echo $wight();

//综上大家应该理解其用法了吧,有时第二个参数为null,有时第三个参数可以不要,这些都跟你匿名函数里 代码中访问的方式紧密相关
//总结:
//1、一般匿名函数中有$this->name类似这样用 $this访问属性方式时,你在使用bind绑定时 ,第二个参数肯定要写,写出你绑定那个对象实例,第三个参数要不要呢,要看你访问的这个属性,在绑定对象中的权限属性,如果是private,protected 你要使用第三个参数 使其变为公有属性, 如果本来就是公有,你可以省略,也可以不省略
//2、一般匿名函数中是 类名::静态属性 类似这样的访问方式(比如例子中A::$weight),你在使用bind绑定时,第二个参数可以写null,也可以写出具体的对象实例,一般写null就行(写了具体对象实例多此一举),第三个参数写不写还是得看你访问的这个静态属性的权限是 private 还是 public,如果是私有private或受保护protected的,你就得第三个参数必须写,才能使其权限变为公有属性 正常访问,如果本来就是公有public可以不用写,可以省略
//
//3、需要提升属性作用域时,第3个参数需要传,传对象或者类名都可以。