闭包
闭包是一种语法结构, 它使你从内部函数访问外部函数作用域. 在js中,函数在每次创建时生成闭包. 闭包创建了一个环境, 这个环境包含了这个闭包创建时所能访问的所有局部变量
function makeFunc() {
var name = "Jack";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
如, 上面例子中, makeFunc() 执行完毕后,name仍然可以在myFunc中被访问
js中的闭包使js实现类似面向对象编程, 因为它允许将函数与其所操作的某些数据(环境)关联起来, 类似于对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联
在这个例子中,我们在函数makeFunc中又定义了函数displayName,并且,内部函数displayName可以引用外部函数makeFunc的参数和局部变量,当nameFunc返回函数displayName时,相关参数和变量都保存在返回的函数中,这就是闭包(Closure)
PHP中的闭包
(1) 实现闭包
首先,闭包在PHP中的实现需要借助匿名函数. 顾名思义,匿名函数没有名字,需要将它返回到一个变量中
$func = function(){
};//有结束符
匿名函数也可以声明参数, 像普通函数一样
$func = function($a){
echo $a;
};
$func('abc'); // 输出: abc
实现闭包: 将匿名函数作为参数传入普通函数, 就实现了一个简单的闭包
$echoStr = function($str){
echo $str;
};
function callFunc($func){
$func('abc');
}
callFunc($echoStr);
也可以直接传递匿名参数, 类似js的写法
callFunc(function($str){
echo $str;
});
此外, 还有在函数内部定义匿名函数并调用的例子
function printStr(){
$funcName = function($a){
echo $a;
};
$funcName('abc');
}
printStr();
// 或者, 将匿名函数返回后,再调用
function printStrFunc(){
$funcName = function($a){
echo $a;
};
return $funcName;
}
$printStr = printStrFunc();
$printStr('abc');
关键字use: 实现闭包对外界变量的引用. PHP默认情况下, 匿名函数不能调用外界变量, 故需要通过use关键字实现对上下文变量的调用
function goAhead(){
$step = 1;
$func = function() use($step) {
echo $step;
$step ++;
};
$func();
echo $step;
}
goAhead();
//输出
//1
//1
上面的情况下调用的是变量的副本, 匿名函数无法改变上下文变量. 如果需要完全引用变量,在变量前加一个 & 符号就可以了
function goAhead2(){
$step = 1;
$func = function() use( &$step ) {
echo $step;
$step ++;
};
$func();
echo $step;
}
goAhead2();
//输出
//1
//2
最后, 如果将匿名函数return返回, 匿名函数将会保存use引用的变量,而外界不能得到这些变量,这样的"闭包"的概念会更清晰
function goAhead3(){
$step = 1;
$func = function() use( &$step ) {
echo $step;
$step ++;
};
return $func;
}
$getStep = goAhead3();
$getStep();
$getStep();
$getStep();
//输出:
//1
//2
//3
(2)闭包的作用
PHP闭包远不及JavaScript闭包强大,而且用class就可代替. PHP闭包的作用有以下几点:
一,减少循环, PHP手册中购物车的例子, getTotal方法用闭包代替了循环
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";
// 最后结果是 54.29
二, 减少参数传递, 使代码易读
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";
};
}
$getHtml = html('h2','hid','hclass');
$html = $getHtml('contents');
echo $html; //输出 <h2 id="hid" class="hclass">contents</h2>
不使用闭包的话, inner参数可能也会在html函数中传入, 代码的阅读和使用不如使用闭包更清晰
三, 解除递归函数, use后的循环函数前需要加 & 符号
$fib =function($n)use(&$fib) {
if($n == 0 || $n == 1) return 1;
return $fib($n - 1) + $fib($n - 2);
};
echo $fib(0) . "\n";// 1
echo $fib(1) . "\n";// 1
echo $fib(2) . "\n";// 2
echo $fib(3) . "\n";// 3
echo $fib(4) . "\n";// 5
echo $fib(5) . "\n";// 8
四, 延迟绑定
$num = 0;
$func1 = function(){
echo $num;
};
$func2 = function()use($num){
echo $num;
};
$func3 = function()use(&$num){
echo $num;
};
$num ++;
$func1();// null, 匿名函数无法调用外界变量
$func2();// 0, 传递的是$num的副本
$func3();// 1, 实现了调用时赋值,延时绑定变量
参考链接:
https://www.cnblogs.com/melonblog/archive/2013/05/01/3052611.html
https://www.cnblogs.com/yjf512/archive/2012/10/29/2744702.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures