php的闭包(Closure)匿名函数详解


Posted in PHP onFebruary 22, 2015

php的闭包(Closure)也就是匿名函数,是PHP5.3引入的。

闭包的语法很简单,需要注意的关键字就只有use,use是连接闭包和外界变量。

$a = function() use($b) {}

简单例子如下:

function callback($fun) {

$fun();

}

$msg = "Hello, everyone";

$fun = function () use($msg) {

print "This is a closure use string value, msg is: $msg. <br />/n";

};

$msg = "Hello, everybody";

callback($fun);

结果是:This is a closure use string value, msg is: Hello, everyone. <br />/n

在PHP新开放的闭包语法中, 我们用use来使用闭包外部定义的变量的。这里我们使用了外部变量$msg,定义完之后,又对其值进行了改变,闭包被执行后输出的是原始值。以传值方式传递的基础类型参数,闭包use的值在闭包创建是就确定了。

小应用如下:

/** 

 * 一个利用闭包的计数器产生器 

 * 这里其实借鉴的是python中介绍闭包时的例子... 

 * 我们可以这样考虑: 

 *      1. counter函数每次调用, 创建一个局部变量$counter, 初始化为1. 

 *      2. 然后创建一个闭包, 闭包产生了对局部变量$counter的引用. 

 *      3. 函数counter返回创建的闭包, 并销毁局部变量, 但此时有闭包对$counter的引用,  

 *          它并不会被回收, 因此, 我们可以这样理解, 被函数counter返回的闭包, 携带了一个游离态的 

 *          变量. 

 *      4. 由于每次调用counter都会创建独立的$counter和闭包, 因此返回的闭包相互之间是独立的. 

 *      5. 执行被返回的闭包, 对其携带的游离态变量自增并返回, 得到的就是一个计数器. 

 * 结论: 此函数可以用来生成相互独立的计数器. 

 */  

function counter() {  

    $counter = 1;  

    return function() use(&$counter) {return $counter ++;};  

}  

$counter1 = counter();  

$counter2 = counter();  

echo "counter1: " . $counter1() . "<br />/n";  

echo "counter1: " . $counter1() . "<br />/n";  

echo "counter1: " . $counter1() . "<br />/n";  

echo "counter1: " . $counter1() . "<br />/n";  

echo "counter2: " . $counter2() . "<br />/n";  

echo "counter2: " . $counter2() . "<br />/n";  

echo "counter2: " . $counter2() . "<br />/n";  

echo "counter2: " . $counter2() . "<br />/n";  

?>

闭包的作用

1. 减少foreach的循环的代码
比如手册http://php.net/manual/en/functions.anonymous.php 中的例子Cart

<?php

// 一个基本的购物车,包括一些已经添加的商品和每种商品的数量。

// 其中有一个方法用来计算购物车中所有商品的总价格。该方法使用了一个closure作为回调函数。

class Cart

{

    const PRICE_BUTTER  = 1.00;

    const PRICE_MILK    = 3.00;

    const PRICE_EGGS    = 6.95;

    protected   $products = array();

    public function add($product, $quantity)

    {

        $this->products[$product] = $quantity;

    }

    public function getQuantity($product)

    {

        return isset($this->products[$product]) ? $this->products[$product] :

               FALSE;

    }

    public function getTotal($tax)

    {

        $total = 0.00;

        $callback =

            function ($quantity, $product) use ($tax, &$total)

            {

                $pricePerItem = constant(__CLASS__ . "::PRICE_" .

                    strtoupper($product));

                $total += ($pricePerItem * $quantity) * ($tax + 1.0);

            };

        //使用用户自定义函数对数组中的每个元素做回调处理

        array_walk($this->products, $callback);

        return round($total, 2);;

    }

}

$my_cart = new Cart;

// 往购物车里添加条目

$my_cart->add('butter', 1);

$my_cart->add('milk', 3);

$my_cart->add('eggs', 6);

// 打出出总价格,其中有 5% 的销售税.

print $my_cart->getTotal(0.05) . "\n";

// The result is 54.29

?>

这里如果我们改造getTotal函数必然要使用到foreach。

2. 减少函数的参数

function html($code , $id="", $class=""){

if ($id !== "") $id = " id = \"$id\"" ;

$class = ($class !== "")? " class =\"$class\">":">";

$open = "<$code$id$class";

$close = "</$code>";

return function ($inner = "") use ($open, $close){

return "$open$inner$close";

    };

}

如果是使用平时的方法,我们会把inner放到html函数参数中,这样不管是代码阅读还是使用都不如使用闭包。

3. 解除递归函数

<?php

$fib = function($n) use(&$fib) {

    if($n == 0 || $n == 1) return 1;

    return $fib($n - 1) + $fib($n - 2);

};

echo $fib(2) . "\n"; // 2

$lie = $fib;

$fib = function(){die('error');};//rewrite $fib variable 

echo $lie(5); // error   because $fib is referenced by closure

注意上题中的use使用了&,这里不使用&会出现错误fib(n-1)是找不到function的(前面没有定义fib的类型)

所以想使用闭包解除循环函数的时候就需要使用

<?php

$recursive = function () use (&$recursive){

// The function is now available as $recursive

}

这样的形式。

4. 延迟绑定

如果你需要延迟绑定use里面的变量,你就需要使用引用,否则在定义的时候就会做一份拷贝放到use中

<?php

$result = 0;

$one = function()

{

    var_dump($result);

};

$two = function() use ($result)

{

    var_dump($result);

};

$three = function() use (&$result)

{

    var_dump($result);

};

$result++;

$one();    // outputs NULL: $result is not in scope

$two();    // outputs int(0): $result was copied

$three();    // outputs int(1)

使用引用和不使用引用就代表了是调用时赋值,还是申明时候赋值

小伙伴们是否对PHP的匿名函数也就是闭包函数有了新的认识了呢,希望本文能给大家一些提示,希望大家能够喜欢。

PHP 相关文章推荐
用C/C++扩展你的PHP 为你的php增加功能
Sep 06 PHP
用PHP编写和读取XML的几种方式
Jan 12 PHP
浅谈web上存漏洞及原理分析、防范方法(文件名检测漏洞)
Jun 29 PHP
PHP封装的一个支持HTML、JS、PHP重定向的多功能跳转函数
Jun 19 PHP
PHP把JPEG图片转换成Progressive JPEG的方法
Jun 30 PHP
PHP实现过滤掉非汉字字符只保留中文字符
Jun 04 PHP
Laravel中Trait的用法实例详解
Mar 16 PHP
老生常谈php 正则中的i,m,s,x,e分别表示什么
Mar 02 PHP
SCP远程VPS快速搬家和WDCP升级php5.3安装memcached和eaccelerator教程
Jul 27 PHP
PHP面向对象五大原则之开放-封闭原则(OCP)详解
Apr 04 PHP
PHP实现的简单组词算法示例
Apr 10 PHP
Laravel Eloquent ORM 多条件查询的例子
Oct 10 PHP
PHP 实现代码复用的一个方法 traits新特性
Feb 22 #PHP
在Windows XP下安装Apache+MySQL+PHP环境
Feb 22 #PHP
PHP+APACHE实现网址伪静态
Feb 22 #PHP
php数组键名技巧小结
Feb 17 #PHP
php使用explode()函数将字符串拆分成数组的方法
Feb 17 #PHP
php使用unset()删除数组中某个单元(键)的方法
Feb 17 #PHP
php实现两个数组相加的方法
Feb 17 #PHP
You might like
php sybase_fetch_array使用方法
2014/04/15 PHP
Yii调试SQL的常用方法
2014/07/09 PHP
php采用file_get_contents代替使用curl实例
2014/11/07 PHP
PHP实现时间比较和时间差计算的方法示例
2017/07/24 PHP
PHPExcel 修改已存在Excel的方法
2018/05/03 PHP
javascript 判断数组是否已包含了某个元素的函数
2010/05/30 Javascript
js将当前时间格式转换成时间搓(自写)
2013/09/26 Javascript
浅谈JavaScript Date日期和时间对象
2014/12/29 Javascript
jQuery CSS3相结合实现时钟插件
2016/01/08 Javascript
js实现获取两个日期之间所有日期的方法
2016/06/17 Javascript
js实现楼层效果的简单实例
2016/07/15 Javascript
JS实现的多张图片轮流播放幻灯片效果
2016/07/22 Javascript
炫酷的js手风琴效果
2016/10/13 Javascript
JS定时检测任务任务完成后执行下一步的解决办法
2016/12/22 Javascript
JavaScript利用闭包实现模块化
2017/01/13 Javascript
js实现简单的获取验证码按钮效果
2017/03/03 Javascript
Vue.js鼠标悬浮更换图片功能
2017/05/17 Javascript
Vue使用高德地图搭建实时公交应用功能(地图 + 附近站点+线路详情 + 输入提示+换乘详情)
2018/05/16 Javascript
微信小程序实现的picker多级联动功能示例
2019/05/23 Javascript
JS实现灯泡开关特效
2020/03/30 Javascript
webpack3.0升级4.0的方法步骤
2020/04/02 Javascript
jQuery实现雪花飘落效果
2020/08/02 jQuery
Nuxt的动态路由和参数校验操作
2020/11/09 Javascript
[03:59]第二届DOTA2亚洲邀请赛选手传记-VGJ.rOtk
2017/04/03 DOTA
[57:29]Alliance vs KG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/17 DOTA
Python机器学习之SVM支持向量机
2017/12/27 Python
对python中raw_input()和input()的用法详解
2018/04/22 Python
详解Django中六个常用的自定义装饰器
2018/07/04 Python
keras .h5转移动端的.tflite文件实现方式
2020/05/25 Python
aec加密 php_php aes加密解密类(兼容php5、php7)
2021/03/14 PHP
柯基袜:Corgi Socks
2017/01/26 全球购物
俄罗斯领先的移动和数字设备在线商店:Svyaznoy.ru
2020/12/21 全球购物
调研座谈会发言材料
2014/08/23 职场文书
店铺转让协议书(2014版)
2014/09/23 职场文书
学生逃课检讨书
2015/02/17 职场文书
2015年教师个人业务工作总结
2015/10/23 职场文书