了,做web开发有一段时间了,大家都知道或者了解JavaScript中有一个非常强大的语法,就是闭包。其实闭包函数的功能在PHP中早就有了。早在PHP5.3版本中,就出现了闭包函数。在7及以后的现代框架中,闭包函数的使用无处不在。下面,就让我们从基础开始,了解一下闭包在PHP中的使用吧!闭包函数(闭包)在PHP中被转换为Closure类的实例。如果在定义的时候赋值给一个变量,需要加上最后的花括号;分号。闭包函数从父作用域继承变量,任何此类变量都应使用use语言构造传入。从PHP7.1开始,不能传入这样的变量:superglobals、$this或与参数同名。基本语法闭包的使用非常简单,与JavaScript非常相似。因为它们都有另一个别名,叫做匿名函数。$a=function(){echo"这是testA";};$a();//这是testAfunctiontestA($a){var_dump($a);}testA($a);//classClosure#1(0){}$b=function($name){echo'thisis'.$name;};$b('鲍勃');//thisisBob我们把$a和$b这两个变量直接赋值给了两个函数。这样,我们就可以使用变量()来调用这两个函数了。通过testA()方法,我们可以看到闭包函数可以作为普通参数传递,因为它会自动转换成Closure类的一个实例。$age=16;$c=function($name){echo'thisis'.$名字。',年龄是'.$age;};$c('查尔斯');//这是Charles,年龄是$c=function($name)use($age){echo'thisis'.$名字。',年龄是'.$age;};$c('查尔斯');//thisisCharles,Ageis16如果我们需要调用外部变量,需要使用use关键字来引用外部变量。这与普通函数不同,因为闭包有严格的作用域问题。对于全局变量,我们可以使用use或者global。但是对于局部变量(函数中的变量),只能用use。这个我们稍后再说。作用域函数testD(){global$testOutVar;echo$testOutVar;}$d=function()使用($testOutVar){echo$testOutVar;};$dd=function(){global$testOutVar;echo$testOutVar;};$testOutVar='这是d';$d();//空测试D();//这是d$dd();//这是d$testOutVar='thisise';$e=function()use($testOutVar){echo$testOutVar;};$e();//这是e$testOutVar='这是ee';$e();//这是e$testOutVar='thisisf';$f=function()use(&$testOutVar){echo$testOutVar;};$f();//这是f$testOutVar='这是ff';$f();//这是在ff范围内,use传递的变量必须在函数定义之前定义,从上面的例子可以看出。如果在变量($testOutVar)之前定义了闭包($d),则$d中use传入的变量为空。同样,我们使用global来测试,无论是普通函数(testD())还是闭包函数($dd),$testOutVar都可以正常使用。$e函数中的变量,在函数定义之后修改,不会影响$e闭包中的变量。这时候就必须使用引用传递($f)来修改闭包中的变量。这里引用传递和值传递的概念和普通函数是一样的。除了变量的使用,闭包函数和普通函数在其他方面基本没有区别,比如类实例化:classG{}$g=function(){global$age;回声$年龄;//16$gClass=newG();变量转储($gClass);//G信息};$g();类的作用域关于全局作用域,闭包函数和普通函数的区别不大,主要区别体现在作为桥接变量传递时的状态。在类方法中,有什么区别吗?$age=18;A级{private$name='A级';publicfunctiontestA(){$insName='测试一个函数';$insstrinsic=function(){var_dump($this);//这个信息echo$this->name;//一个类echo$age;//空回显$insName;//无效的};$内在();$instrinsic1=function(){global$age,$insName;回声$年龄;//18echo$insName;//无效的};$内在1();全球$年龄;$instrinsic2=function()use($age,$insName){echo$age;//18回显$insName;//测试一个函数};$insstrinsic2();}}$aClass=newA();$aClass->testA();A::testA()方法中的$insName变量只能通过use获取。闭包函数中的$this是调用它的环境的上下文,在本例中是类A本身。闭包的父范围是定义它的函数(不一定是调用它的函数)。静态闭包函数不能获取$this。仍然可以使用global获取全局变量。Tips了解了闭包的这些特点之后,我们可以看几个小技巧:$arr1=[['name'=>'Asia'],['name'=>'Europe'],['name'=>'America'],];$arr1Params='很好!';//foreach($arr1as$k=>$a){//$arr1[$k]=$a.$arr1Params;//}//print_r($arr1);array_walk($arr1,function(&$v)use($arr1Params){$v.='很好!';});print_r($arr1);killforeach:很多数组类函数,比如array_map、array_walk等,都需要用闭包函数处理。在上面的例子中,我们使用array_walk来处理数组的内容。是不是很有函数式编程的感觉,而且很清晰。functiontestH(){returnfunction($name){echo"thisis".$名称;};}testH()("testH的闭包!");//这是testH的闭包!看到这样的代码Circled,不要一头雾水。PHP7支持立即执行语法,即JavaScript中的IIFE(Immediately-invokedfunctionexpression)。让我们再做一个计算斐波那契数列:$fib=function($n)use(&$fib){if($n==0||$n==1){return1;}返回$fib($n-1)+$fib($n-2);};echo$fib(10);使用递归也是如此。这里直接换成闭包递归来实现。最后要注意的是use中传递的变量名不能是带下标的数组项:$fruits=['apples','oranges'];$example=function()use($fruits[0]){//解析错误:语法错误,意外的'[',期待','或')'echo$fruits[0];};$例子();这直接就是语法错误,无法成功运行。彩蛋在Laravel中的IoC服务容器中,使用了大量的闭包能力,我们模拟一个给大家理解。当然,更好的解决办法是自己过一遍Laravel的源码。B类{}C类{}D类{}Ioc类{public$objs=[];公共$容器=[];公共函数__construct(){$this->objs['b']=function(){returnnewB();};$this->objs['c']=function(){returnnewC();};$this->objs['d']=function(){returnnewD();};}publicfunctionbind($name){if(!isset($this->containers[$name])){if(isset($this->objs[$name])){$this->容器[$name]=$this->objs[$name]();}else{返回空值;}}返回$this->containers[$name];}}$ioc=newIoc();$bClass=$ioc->bind('b');$cClass=$ioc->bind('c');$dClass=$ioc->bind('d');$eClass=$ioc->bind('e');var_dump($bClass);//Bvar_dump($cClass);//Cvar_dump($dClass);//Dvar_dump($eClass);//NULL总结闭包特性经常出现的地方就是事件回调类的函数,另外就是彩蛋中IoC的实现。因为闭包有很强大的懒加载能力。在IoC示例中,闭包中返回的是新对象。当我们的程序运行时,如果不调用$ioc->bind('b'),那么B对象就不会被创建,也就是说此时不会占用资源和内存。而当我们需要它的时候,我们只在从服务容器中取出对象时才使用闭包来真正创建对象。同理,事件的回调也是同一个概念。当事件发生时,我们只在需要处理的时候才执行回调中的代码。如果没有闭包的概念,那么$objs容器是这样写的:$this->objs['b']=newB();$this->objs['c']=newC();$this->objs['d']=newD();实例化容器时,必须实例化所有类。这样就为程序创建了很多不用的对象,带来了非常大的资源浪费。基于闭包的强大能力,现在在Laravel、TP6等框架中,闭包函数无处不在。学无止境,掌握原理再学习框架往往事半功倍。测试代码:https://github.com/zhangyue0503/dev-blog/blob/master/php/201911/source/%E8%BF%98%E4%B8%8D%E7%9F%A5%E9%81%93PHP%E6%9C%89%E9%97%AD%E5%8C%85%EF%BC%9F%E9%82%A3%E4%BD%A0%E7%9C%9FOUT%E4%BA%86。PHP参考文档:https://www.php.net/manual/zh/functions.anonymous.phphttps://www.php.net/manual/zh/functions.anonymous.php#100545https://www.php。net/manual/zh/functions.anonymous.php#119388各媒体平台均可搜索【硬核项目经理】
