闭包匿名函数,还在傻傻搞不清楚吗?

闭包#

还在正月里,首先祝大家新年快乐!本文是从 PHP 角度来看闭包与匿名函数。

闭包基础#

闭包长啥样#

以下是最常见的闭包形式,其他方法创建的闭包在此就不做说明了


$helloWorld = function() {
    return 'hello world';
};

var_dump($helloWorld);

/*
返回结果如下:
object(Closure)#1 (0) {
}
*/

定义闭包#

闭包 = 匿名函数 = 实例 = 对象。 类代码大概如下:


// 闭包类
class Closure{
    // 禁止实例化
    private __construct()
    {

    }

    // 复制一个闭包,绑定指定的 $newThis 对象和类的作用域
    public static function bind(Closure $closure, object $newThis, mixed $newscope = 'static') :Closure
    {
        // TO-DO
    }

    // 复制当前闭包对象, 绑定指定的 $newThis 对象和类的作用域 
    public function bindTo(object $newThis, mixed $newscope = 'static') :Closure
    {
        // TO-DO
    }

    // 魔术方法,无作用
    public function __invoke()
    {
        // TO-DO
    }
}
方法 说明
__construct 用于禁止实例化的构造方法
bind 复制一个闭包,绑定指定的 $this 对象和类作用域
bindTo 复制当前闭包,绑定指定的 $this 对象和类作用域

真实应用闭包#

例如我们代码中需要将数组值转化 int 类型

$params = ['10', '20', '30', '40'];

$newArray = array_map(function($item) {
    return intval($item);
}, $params);

var_dump($newArray);

/*
返回结果如下:
array(4) {
  [0]=>
  int(10)
  [1]=>
  int(20)
  [2]=>
  int(30)
  [3]=>
  int(40)
}

*/

闭包高级应用#

Closure::bind#

通俗点理解就是让我们的闭包代码块的作用域在某一类中或者对象中。使得闭包中的 $this-> self:: 类名:: 能指定到对象或者是类

代码块#


/**
 * Closure class a method
 * 复制一个闭包,绑定指定的 $newThis对象和类的作用域
 *
 * @param Closure $closure
 * @param object $newThis
 * @param mixed $newscope
 * @return \Closure
 */
public static function bind(Closure $closure, object $newThis, mixed $newscope = 'static') :Closure
{
    // TO-DO
}

参数说明#

PHP 手册 中有以下几句可加深我们理解:

创建并返回一个 匿名函数, 它与当前对象的函数体相同、绑定了同样变量,但可以绑定不同的对象,也可以绑定新的类作用域。

“绑定的对象” 决定了函数体中的 $this 的取值,“类作用域” 代表一个类型、决定在这个匿名函数中能够调用哪些 私有 和 保护 的方法。 也就是说,此时 $this 可以调用的方法,与 newscope 类的成员函数是相同的。

静态闭包不能有绑定的对象( newthis 参数的值应该设为 NULL)不过仍然可以用 bubdTo 方法来改变它们的类作用域。


参数 说明
$closure 表示闭包函数
$newThis 闭包中 $this 所指的对象
$newscope 我们闭包中需要操作属性等所属类的类型名

代码检验真理#

class Order
{
    private $orderId = '001';

    private static $defaultMoney = '100';

    public $num = 1;
}

$getOrderId = function() {
    return $this->orderId;
};

$getNum = function() {
    return $this->num;
};

$getDefaultMoney = static function() {
    return Order::$defaultMoney;
};
1. Closure::bind($closure,null, 'Order')#

给闭包绑定了 Order 类的作用域,但未绑定闭包 $this 对象 (Order::class == new Order () == 'Order')

$getDefaultMoney1 = Closure::bind($getDefaultMoney, null, Order::class);
// 输出:100

$getOrderId1 = Closure::bind($getOrderId, null, Order::class);
// 输出:PHP Fatal error:  Uncaught Error: Using $this when not in object context

$getNum1 = Closure::bind($getNum, null, Order::class);
// 输出:PHP Fatal error:  Uncaught Error: Using $this when not in object context
2. Closure::bind($closure,$object, 'Order')#

给闭包绑定了 Order 类的作用域,将 Order 实例绑定闭包 $this 对象

$getDefaultMoney2 = Closure::bind($getDefaultMoney, new Order(), Order::class);
// 输出:PHP Warning:  Cannot bind an instance to a static closure

$getOrderId2 = Closure::bind($getOrderId, new Order(), Order::class);
// 输出 string(3) "001"

$getNum2 = Closure::bind($getNum, new Order(), Order::class);
// 输出 int(1)
3. Closure::bind($closure,$object)#

将 Order 实例对象作为 $this 对象绑定给闭包,保留闭包原有作用域

$getDefaultMoney3 = Closure::bind($getDefaultMoney, new Order());
// 输出 PHP Warning:  Cannot bind an instance to a static closure

$getOrderId3 = Closure::bind($getOrderId, new Order());
// 输出 Fatal error: Uncaught Error: Cannot access private property Order::$orderId

$getNum3 = Closure::bind($getNum, new Order());
// 输出 int(1)

结论#

$newThis $newscope 结果
null Order::class 可调用 Order 类作用域私有 (受保护) 的静态属性
new Order() Order::class 可调用该对象的私有 (受保护) 的属性
new Order() 默认值 可调用类作用域公共的属性

$closure->bindTo#

Closure::bind 相同。一个是静态版,一个是非静态版。具体使用可以参考上面

$getOrderId1 = $getOrderId->bindTo(new Order(), Order::class);

小任务#

利用闭包在不修改 Order 类的前提下增加 getOrderId 功能。最后实现的是如下两行代码调用方法。大家可以思考下

$order = new Order();
echo $order->getOrderId();

// 代码段
function getOrderId()
{
    return $this->orderId;
}

闭包 == 匿名函数#

感谢 @Wi1dcard 补充: 在 PHP 内,由于匿名函数是通过闭包类实现的( Anonymous functions are implemented using the Closure class),因此多数人混淆了闭包技术和匿名函数;实际上,在其他语言内是完全不同的两个概念的。

参考:
http://php.net/manual/en/functions.anonymo...
https://stackoverflow.com/questions/491211...

闭包是一项「技术」或者说「功能」,能够捕获并存储当前当前上下文状态,以供后续使用。
匿名函数就只是一个「函数」,一个没有名字的函数而已。
在实际应用中,匿名函数通常伴随着使用闭包技术;但闭包并不一定只能用在匿名函数内。

相关链接#

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 6年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 9

搭个车补充一下:在 PHP 内,由于匿名函数是通过闭包类实现的(Anonymous functions are implemented using the Closure class),因此多数人混淆了闭包技术和匿名函数;实际上,在其他语言内是完全不同的两个概念的。

参考:

闭包是一项「技术」或者说「功能」,能够捕获并存储当前当前上下文状态,以供后续使用。

匿名函数就只是一个「函数」,一个没有名字的函数而已。

在实际应用中,匿名函数通常伴随着使用闭包技术;但闭包并不一定只能用在匿名函数内。

6年前 评论

仿照 PHP 手册写的,请大家看下

代码已被折叠,点此展开
6年前 评论

仿照 PHP 手册写的,请大家看下

代码已被折叠,点此展开
6年前 评论

真棒!

6年前 评论

搭个车补充一下:在 PHP 内,由于匿名函数是通过闭包类实现的(Anonymous functions are implemented using the Closure class),因此多数人混淆了闭包技术和匿名函数;实际上,在其他语言内是完全不同的两个概念的。

参考:

闭包是一项「技术」或者说「功能」,能够捕获并存储当前当前上下文状态,以供后续使用。

匿名函数就只是一个「函数」,一个没有名字的函数而已。

在实际应用中,匿名函数通常伴随着使用闭包技术;但闭包并不一定只能用在匿名函数内。

6年前 评论

@Wi1dcard 多谢赐教。PHP 手册中也写道了如此。我能否将你的见解补充到文章后面呢?

6年前 评论

@悲剧不上演 当然可以,开源社区,大家互相学习~

6年前 评论
kkk1

nice

6年前 评论
九霄道长

mark

3年前 评论
九霄道长

这么好的文章我居然一开始搜不到

3年前 评论