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 相关文章推荐
php中可能用来加密字符串的函数[base64_encode、urlencode、sha1]
Jan 16 PHP
解析关于java,php以及html的所有文件编码与乱码的处理方法汇总
Jun 24 PHP
处理(php-cgi.exe - FastCGI 进程超过了配置的请求超时时限)的问题
Jul 03 PHP
ThinkPHP利用PHPMailer实现邮件发送实现代码
Sep 26 PHP
linux使用crontab实现PHP执行计划定时任务
May 10 PHP
完美的2个php检测字符串是否是utf-8编码函数分享
Jul 28 PHP
php实现的mongodb操作类
May 28 PHP
PHP表单提交后引号前自动加反斜杠的原因及三种办法关闭php魔术引号
Sep 30 PHP
老生常谈ThinkPHP中的行为扩展和插件(推荐)
May 05 PHP
php单元测试phpunit入门实例教程
Nov 17 PHP
Laravel使用scout集成elasticsearch做全文搜索的实现方法
Nov 30 PHP
php7 list()、session及其他模块的修改实例分析
May 25 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树的深度编历生成迷宫及A*自动寻路算法实例分析
2015/03/10 PHP
PHP+MySQL实现输入页码跳转到指定页面功能示例
2018/06/01 PHP
详解PHP实现支付宝小程序用户授权的工具类
2018/12/25 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
js类中获取外部函数名的方法
2007/08/19 Javascript
jquery下将选择的checkbox的id组成字符串的方法
2010/11/28 Javascript
jQuery对表单的操作代码集合
2011/04/06 Javascript
Knockoutjs快速入门(经典)
2012/12/24 Javascript
放弃用你的InnerHTML来输出HTML吧 jQuery Tmpl不详细讲解
2013/04/20 Javascript
seajs中模块的解析规则详解和模块使用总结
2014/03/12 Javascript
js随机生成网页背景颜色的方法
2015/02/26 Javascript
JavaScript实现算术平方根算法-代码超简单
2015/09/11 Javascript
浅谈JS原型对象和原型链
2016/03/02 Javascript
jquery中取消和绑定hover事件的实现代码
2016/06/02 Javascript
jQuery 限制输入字符串长度
2016/06/20 Javascript
js严格模式总结(分享)
2016/08/22 Javascript
在 Vue 项目中引入 tinymce 富文本编辑器的完整代码
2018/05/04 Javascript
Javascript实现购物车功能的详细代码
2018/05/08 Javascript
vue 实现搜索的结果页面支持全选与取消全选功能
2019/05/10 Javascript
Vue.js标签页组件使用方法详解
2019/10/19 Javascript
js实现无刷新监听URL的变化示例代码详解
2020/06/03 Javascript
Python实现二分法算法实例
2015/02/02 Python
Python实现将sqlite数据库导出转成Excel(xls)表的方法
2017/07/17 Python
python OpenCV学习笔记之绘制直方图的方法
2018/02/08 Python
详解Python3网络爬虫(二):利用urllib.urlopen向有道翻译发送数据获得翻译结果
2019/05/07 Python
python 函数中的内置函数及用法详解
2019/07/02 Python
anaconda如何查看并管理python环境
2019/07/05 Python
如何使用Python多线程测试并发漏洞
2019/12/18 Python
上班睡觉检讨书
2014/01/09 职场文书
医生进修自我鉴定
2014/01/19 职场文书
护士求职自荐信范文
2014/03/19 职场文书
村党支部书记承诺书
2014/05/29 职场文书
四风问题个人对照检查材料
2014/09/26 职场文书
初婚未育证明样本
2014/10/24 职场文书
手把手教你实现PyTorch的MNIST数据集
2021/06/28 Python
Python编程源码报错解决方法总结经验分享
2021/10/05 Python