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 相关文章推荐
用Apache反向代理设置对外的WWW和文件服务器
Oct 09 PHP
PHP 远程文件管理,可以给表格排序,遍历目录,时间排序
Aug 07 PHP
PHP数据类型之布尔型的介绍
Apr 28 PHP
用PHP实现弹出消息提示框的两种方法
Dec 17 PHP
php使用websocket示例详解
Mar 12 PHP
PHP数组去重比较快的实现方式
Jan 19 PHP
PHP二维数组排序简单实现方法
Feb 14 PHP
PHP中的密码加密的解决方案总结
Oct 26 PHP
PHP基于自定义函数生成笛卡尔积的方法示例
Sep 30 PHP
PHP调用微博接口实现微博登录的方法示例
Sep 22 PHP
PHP实现小程序批量通知推送
Nov 27 PHP
Laravel 自动生成验证的实例讲解:login / logout
Oct 14 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
星际流派综述
2020/03/04 星际争霸
php实现的一个简单json rpc框架实例
2015/03/30 PHP
thinkphp5.0自定义验证规则使用方法
2017/11/16 PHP
PHP的new static和new self的区别与使用
2019/11/27 PHP
javascript之函数直接量(function(){})()
2007/06/29 Javascript
javascript中的继承实例代码
2011/04/27 Javascript
javascript获取浏览器类型和版本的方法(js获取浏览器版本)
2014/03/13 Javascript
Vue关于数据绑定出错解决办法
2017/05/15 Javascript
Nodejs模块载入运行原理
2018/02/23 NodeJs
vue2.0获取鼠标位置的方法
2018/09/13 Javascript
Vue中登录验证成功后保存token,并每次请求携带并验证token操作
2020/09/08 Javascript
[56:17]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第三场 8.22
2019/09/05 DOTA
python爬取NUS-WIDE数据库图片
2016/10/05 Python
浅谈Python爬取网页的编码处理
2016/11/04 Python
Python常见读写文件操作实例总结【文本、json、csv、pdf等】
2019/04/15 Python
python制作填词游戏步骤详解
2019/05/05 Python
python GUI实现小球满屏乱跑效果
2019/05/09 Python
python错误调试及单元文档测试过程解析
2019/12/19 Python
pycharm实现在子类中添加一个父类没有的属性
2020/03/12 Python
推荐一些比较有用的css3新属性
2014/11/11 HTML / CSS
详解Canvas 实现炫丽的粒子运动效果(粒子生成文字)
2018/02/01 HTML / CSS
STRATHBERRY苏贝瑞包包官网:西班牙高级工匠手工打造
2020/11/10 全球购物
请说出你所知道的线程同步的方法
2013/04/19 面试题
工程管理英文求职信
2014/03/18 职场文书
婚前保证书
2014/04/29 职场文书
中班幼儿评语大全
2014/04/30 职场文书
2015年度物流工作总结
2015/04/30 职场文书
反邪教警示教育活动总结
2015/05/09 职场文书
养成教育工作总结
2015/08/13 职场文书
2016年11月份红领巾广播稿
2015/12/21 职场文书
关于golang高并发的实现与注意事项说明
2021/05/08 Golang
python文本处理的方案(结巴分词并去除符号)
2021/05/26 Python
NGINX 权限控制文件预览和下载的实现原理
2022/01/18 Servers
mapstruct的用法之qualifiedByName示例详解
2022/04/06 Java/Android
CentOS MySql8 远程连接实战
2022/04/19 MySQL
Mysql查询时间区间日期列表,不会由于数据表数据影响
2022/04/19 MySQL