use传值时闭包获取变量快照,后续修改互不影响;传引用时共享内存地址,修改实时同步;对象因句柄特性天然可修改属性,无需引用;循环变量需显式绑定或立即执行避免复用陷阱。

use传值时,闭包拿到的是快照副本
定义闭包那一瞬间,use ($x) 拷贝的是 $x 当前的值,后续外部修改 $x 不会影响闭包内部看到的值,闭包内部修改也不会反馈回去。
常见错误现象:以为递增计数器能改外部变量,结果没变:
$counter = 0;
$inc = function() use ($counter) {
$counter++;
};
$inc();
echo $counter; // 输出 0,不是 1
- 适用于只读场景,比如固定配置、初始状态、模板参数
- 对简单类型(int/string/bool)安全;对数组也按值复制(深拷贝语义,PHP 7.4+ 后更严格)
- 性能开销略高(尤其大数组或对象,但多数情况可忽略)
use传引用时,闭包和外部共享同一内存地址
use (&$x) 让闭包和外部变量指向同一个 zval,任何一方修改都会立刻反映到另一方。
典型误用:在循环中不加区分地 use (&$item),导致所有闭包最终都引用最后一次迭代的值(PHP 8.2 前尤其危险):
$handlers = [];
foreach (['a', 'b', 'c'] as $item) {
$handlers[] = function() use (&$item) {
echo $item;
};
}
foreach ($handlers as $h) $h(); // 全部输出 'c'
- 必须明确需要双向同步时才用引用,比如计数器、状态标记、临时收集器
- 对象本身是引用语义,
use ($obj)已能修改其属性,无需额外加&;加了反而可能引发意外别名问题 - PHP 8.2 起 foreach 中的
use (&$var)行为已修复,但旧版本仍需手动克隆或用键值替代
对象传入use时不加&也能被修改,但本质不同
对象变量本身存储的是句柄(object handle),use ($obj) 复制的是这个句柄,不是对象实体。所以调用 $obj->method() 或修改属性,外部对象会同步变化——但这不是“引用传递”,而是 PHP 对象的天然行为。
验证方式:
$obj = new stdClass();
$obj->val = 1;
$fn = function() use ($obj) {
$obj->val = 99;
};
$fn();
var_dump($obj->val); // 输出 int(99)
- 此时加
&$obj只会让变量名本身也被共享(比如重新赋值$obj = new stdClass()会影响外部) - 若想彻底隔离对象,需显式克隆:
use ($obj) { $obj = clone $obj; ... } - 数组、字符串、整数等非对象类型,不加
&就绝对隔离,加了才打通
use不能捕获超出生命周期的变量,尤其是循环变量
PHP 8.2 之前,在 foreach 或 for 循环里直接 use ($item),所有闭包会共享同一个栈变量地址,导致最后都读到末次值。这不是 bug,是变量复用机制决定的。
安全写法只有两种:
- 用键值对显式绑定:
use ($items[$key])或use ($key, $items) - 在循环内立即执行并捕获,避免延迟调用:
$handlers[] = (function($i) use ($items) { return $items[$i]; })($key);
最易被忽略的一点:即使你没写 &,只要用了循环变量且闭包延迟执行,就大概率踩坑——这不是传值/传引用的选择问题,而是变量作用域生命周期本身的问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











