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 相关文章推荐
用文本作数据处理
Oct 09 PHP
PHP学习之输出字符串(echo,print,printf,print_r和var_dump)
Apr 17 PHP
php数组函数序列之ksort()对数组的元素键名进行升序排序,保持索引关系
Nov 02 PHP
浅析PHP原理之变量(Variables inside PHP)
Aug 09 PHP
PHP判断一个gif图片是否为动态图片的方法
Nov 19 PHP
php+mysql大量用户登录解决方案分析
Dec 29 PHP
PHP使用内置dir类实现目录遍历删除
Mar 31 PHP
php异常处理方法实例汇总
Jun 24 PHP
PHP封装CURL扩展类实例
Jul 28 PHP
Smarty高级应用之缓存操作技巧分析
May 14 PHP
php的扩展写法总结
May 14 PHP
layui数据表格自定义每页条数limit设置
Oct 26 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相关资料
2006/10/09 PHP
PHP通过COM使用ADODB的简单例子
2006/12/31 PHP
php select,radio和checkbox默认选择的实现方法
2010/05/15 PHP
destoon实现调用当前栏目分类及子分类和三级分类的方法
2014/08/21 PHP
php 使用html5实现多文件上传实例
2016/10/24 PHP
js判断输入是否为正整数、浮点数等数字的函数代码
2010/11/17 Javascript
非html5实现js版弹球游戏示例代码
2013/09/22 Javascript
使表格的标题列可左右拉伸jquery插件封装
2014/11/24 Javascript
JavaScript中使用指数方法Math.exp()的简介
2015/06/15 Javascript
jQuery中extend函数详解
2015/07/13 Javascript
遍历json 对象的属性并且动态添加属性的实现
2016/12/02 Javascript
微信小程序实现红包功能(后端PHP实现逻辑)
2018/07/11 Javascript
js正则表达式校验指定字符串的方法
2018/07/23 Javascript
基于jQuery实现挂号平台首页源码
2020/01/06 jQuery
python中__call__方法示例分析
2014/10/11 Python
Python缩进和冒号详解
2016/06/01 Python
Python简单计算文件MD5值的方法示例
2018/04/11 Python
使用python编写监听端
2018/04/12 Python
python 常见字符串与函数的用法详解
2018/11/23 Python
对python内置map和six.moves.map的区别详解
2018/12/19 Python
详解用Python实现自动化监控远程服务器
2019/05/18 Python
Python-接口开发入门解析
2019/08/01 Python
CSS书写规范、顺序和命名规则
2014/03/06 HTML / CSS
HTML5+css3:3D旋转木马效果相册
2017/01/03 HTML / CSS
Ariat英国官网:为世界顶级马术运动员制造最优质的鞋类和服装
2020/02/14 全球购物
经典c++面试题二
2015/08/14 面试题
小学运动会表扬稿
2014/01/19 职场文书
《回乡偶书》教学反思
2014/04/12 职场文书
小学生志愿者活动方案
2014/08/23 职场文书
战略性融资合作协议书范本
2014/10/17 职场文书
劳动争议仲裁代理词
2015/05/25 职场文书
长江七号观后感
2015/06/11 职场文书
2016简单的租房合同范本
2016/03/18 职场文书
2019 入党申请书范文
2019/07/10 职场文书
使用pandas模块实现数据的标准化操作
2021/05/14 Python
Redis特殊数据类型bitmap位图
2022/06/01 Redis