我有此代码:
$originalBar = [
'baz' => 18
];
function modify (&$bar) {
$bar['test'] = true;
$bar = [
'data' => 42
];
global $originalBar;
echo 'same ' . ($bar === $originalBar) . PHP_EOL;
}
modify($originalBar);
var_dump($originalBar);
我知道,由于该函数通过引用接受参数,因此任何传递的数组都将被修改。所以我希望通过以下方式更改原始数组:
$bar['test'] = true;
...并将test
的键$originalBar
设置为true
。但是,当我重新分配$bar
时,我希望变量不再指向原始数组($originalBar
),并且以后的任何更改都不会对其进行修改。显然,情况并非如此,因为输出为:
same 1
array(1) {
["data"]=>
int(42)
}
通过重新分配$bar
,我也重新分配了$originalBar
。我希望它的功能与JavaScript中的功能相同,这就是为什么我一开始很困惑的原因。
我的问题是-是否在某处记录了该文件?我阅读了Passing by Reference文档,但在那没找到。
编辑:如果不是在 modify 函数中执行此操作:
$bar = [
'data' => 42
];
...我这样做:
$arr = [
'data' => 42
];
$bar = &$arr;
...我得到了最初的预期结果:
same
array(2) {
["baz"]=>
int(18)
["test"]=>
bool(true)
}
有趣的是,在这种情况下,$originalBar
未分配给$arr
的值,而是保留其旧值。
答案 0 :(得分:2)
我想纠正对问题,评论和自我回答的误解。
42
-但可以使您变异对象-您可以调用方法或设置属性,并且函数外的代码将能够看到结果。$foo=42;
或$foo++;
也会发生同样的事情。当您通过引用分配($foo =& $bar
)时,PHP中的情况变得更加不常见;想到它的一种方法是,您有一个变量,并为其指定了两个名称。
我更喜欢将您写成$bar = &$newValue
的内容拼写为$bar =& $newValue
,因为该操作并不是真正的“为$bar
分配内容”,而是“绑定名称$bar
作为某物的别名”。在绑定该别名时,它将丢弃该名称的所有先前绑定,因此它将撤消该名称的“引用传递”性质。
答案 1 :(得分:2)
一个可能的混乱点是,PHP中的数组与JS中的数组/对象不同,出于传递值的目的,它们的行为类似于字符串或数字。
在PHP中,按值传递的数组在弄脏函数时将为copied on write,就像字符串或数字类型一样:
function modify($a) {
global $foo;
var_dump($a === $foo); # => true
$a['hello'] = "world";
var_dump($a === $foo); # => false, we wrote to $a and it was copied.
}
$foo = ["baz" => 42];
modify($foo);
var_dump($foo); # => ["baz" => 42] (the original was unchanged after the function call)
从JS的角度来看,我们可能希望$a['hello'] = "world";
会反映在外部对象上而不导致创建副本:
const modify = a => {
console.log(a === foo); // => true
a.hello = "world";
console.log(a === foo); // => true
};
const foo = {bar: "baz"};
modify(foo);
console.log(foo); // => {"bar": "baz", "hello": "world"}
PHP中的按值传递行为不足为奇:
class A {
function __construct() {
$this->bar = "hello";
}
}
function modify($a) {
global $foo;
var_dump($a === $foo); # => true
$a->bar = "world";
var_dump($a === $foo); # => true
}
$foo = new A();
modify($foo);
var_dump($foo); /* => object(A)#1 (1) {
["bar"]=>
string(5) "world"
}
*/
在PHP中,通过引用传递可对原始数组进行突变:
function modify(&$a) {
global $foo;
var_dump($a === $foo); # => true
$a['hello'] = "world";
var_dump($a === $foo); # => true
}
$foo = ["baz" => 42];
modify($foo);
print_r($foo); # => ["baz" => 42, "hello" => "world"]
参考变量也可以重新分配给新值:
function modify(&$a) {
global $foo;
var_dump($a === $foo); # => true
$a = "world";
var_dump($a === $foo); # => true
}
$foo = ["baz" => 42];
modify($foo);
print_r($foo); # => "world"
由于JS不支持通过引用进行传递,因此除了使用引用运算符来支持函数内数组的JS /类对象变异之外,JS和PHP在这些行为上没有明显的平行之处。
答案 2 :(得分:1)
因为它是预期的行为,所以未在任何地方进行记录。我只是看错了。 PHP文档说:
您可以通过引用将变量传递给函数,以便该函数可以修改变量。
我有一个错误的印象,因为对象默认是在JavaScript中通过引用传递的,在那里,重新分配参数变量不会更改原始变量。
在PHP中通过引用传递的目的是能够修改变量,而不仅仅是其指向的对象。这意味着,您不仅可以更改对象,还可以重新分配传递的变量。
正如@ggorlen在对我的问题的评论中所说:
引用运算符实际上在做两件事:1)公开原始内存位置以进行重新分配; 2)允许对诸如数组之类的复杂结构进行突变。在JS中,第一个永远不可能,而第二个永远都是可能的。在这些方面,PHP提供了一些“灵活性”,一方面,
function ($var)
比大多数lang更具限制性,而function (&$var)
则比大多数语言更宽松,这并不完全直观。