PHP的传值方式
PHP赋值类型分为两种,传值和传引用,今天我们主要讨论这两种方式在使用上的异同。
普通赋值
<?php
// Integer
$a = 1111;
$b = $a;
echo $a.PHP_EOL; // echo 1111
echo $b.PHP_EOL; // echo 1111
$a = 2222;
echo $a.PHP_EOL; // echo 2222
echo $b.PHP_EOL; // echo 1111
// Boolean
$a = TRUE;
$b = $a;
var_dump($a); // echo boolean true
var_dump($b); // echo boolean true
$a = FALSE;
var_dump($a); // echo boolean false
var_dump($b); // echo boolean true
// Float
$a = 11.11;
$b = $a;
echo $a.PHP_EOL; // echo 11.11
echo $b.PHP_EOL; // echo 11.11
$a = 22.22;
echo $a.PHP_EOL; // echo 22.22
echo $b.PHP_EOL; // echo 11.11
// Array
$a = ['1111', '2222'];
$b = $a;
print_r($a); // echo Array ( [0] => 1111 [1] => 2222 )
print_r($b); // echo Array ( [0] => 1111 [1] => 2222 )
$a[0] = '3333';
$a[1] = '4444';
print_r($a); // echo Array ( [0] => 3333 [1] => 4444 )
print_r($b); // echo Array ( [0] => 1111 [1] => 2222 )
// Object
class obj{
public $field = '1111';
}
$a = new obj();
$b = $a;
var_dump($a); // echo object(obj)[1] public 'field' => string '1111' (length=4)
var_dump($b); // echo object(obj)[1] public 'field' => string '1111' (length=4)
$a->field = '2222';
var_dump($a); // echo object(obj)[1] public 'field' => string '2222' (length=4)
var_dump($b); // echo object(obj)[1] public 'field' => string '2222' (length=4)
对于普通类型来说(Integer,Float,Boolean,Array),变量赋值类型为值赋值,相当于变量的一份拷贝,赋值后原变量修改不会影响后赋值的变量,对于对象来说,变量赋值为引用传递,传递的是变量的地址,修改变量会影响所有指向这个对象的引用。在函数中使用时同理,普通类型传递值,对象传递引用,函数内对对象的修改会影响函数外的变量,如果确实要传递这个对象而又不想影响外部变量可以考虑使用clone关键字。如下示例:
<?php
class obj{
public $field = '1111';
}
function test($param) {
$param->field = '2222';
}
$a = new obj();
$b = $a;
var_dump($a); // echo object(obj)[1] public 'field' => string '1111' (length=4)
var_dump($b); // echo object(obj)[1] public 'field' => string '1111' (length=4)
test($b);
var_dump($a); // echo object(obj)[1] public 'field' => string '2222' (length=4)
var_dump($b); // echo object(obj)[1] public 'field' => string '2222' (length=4)
$c = new obj();
$d = clone $c;
var_dump($c); // echo object(obj)[1] public 'field' => string '1111' (length=4)
var_dump($d); // echo object(obj)[1] public 'field' => string '1111' (length=4)
test($d);
var_dump($c); // echo object(obj)[1] public 'field' => string '1111' (length=4)
var_dump($d); // echo object(obj)[1] public 'field' => string '2222' (length=4)
传递引用
引用传递采用需要在变量前添加&符号,示例:
<?php
// Integer
$a = 1111;
$b = &$a;
echo $a.PHP_EOL; // echo 1111
echo $b.PHP_EOL; // echo 1111
$a = 2222;
echo $a.PHP_EOL; // echo 2222
echo $b.PHP_EOL; // echo 2222
// Boolean
$a = TRUE;
$b = &$a;
var_dump($a); // echo boolean true
var_dump($b); // echo boolean true
$a = FALSE;
var_dump($a); // echo boolean false
var_dump($b); // echo boolean false
// Float
$a = 11.11;
$b = &$a;
echo $a.PHP_EOL; // echo 11.11
echo $b.PHP_EOL; // echo 11.11
$a = 22.22;
echo $a.PHP_EOL; // echo 22.22
echo $b.PHP_EOL; // echo 22.22
// Array
$a = ['1111', '2222'];
$b = &$a;
print_r($a); // echo Array ( [0] => 1111 [1] => 2222 )
print_r($b); // echo Array ( [0] => 1111 [1] => 2222 )
$a[0] = '3333';
$a[1] = '4444';
print_r($a); // echo Array ( [0] => 3333 [1] => 4444 )
print_r($b); // echo Array ( [0] => 3333 [1] => 4444 )
// Object
class obj{
public $field = '1111';
}
$a = new obj();
$b = &$a;
var_dump($a); // echo object(obj)[1] public 'field' => string '1111' (length=4)
var_dump($b); // echo object(obj)[1] public 'field' => string '1111' (length=4)
$a->field = '2222';
var_dump($a); // echo object(obj)[1] public 'field' => string '2222' (length=4)
var_dump($b); // echo object(obj)[1] public 'field' => string '2222' (length=4)
当传递引用时,对变量的操作会影响所有的引用该值得变量,对于对象来说本身就是引用传递,所以没有任何区别。按值传递时,php必须复制值。特别是对于大型的字符串和数组来说,这将会是一个代价很大的操作。按引用传递则不需要复制值,对于性能提高很有好处,在使用过程中应该按照实际情况选择引用传递或者值传递。对于函数传参来说,也是一样,只需要在参数前边添加&符。
实战
说了这么多了,是骡子是马拉出来溜溜,看下面这道题:
<?php
$arr = [1, 2, 3];
foreach ($arr as &$v) {
// no enything
}
foreach ($arr as $v) {
// no enything
}
prin_r($arr); // echo Array ( [0] => 1 [1] => 2 [2] => 2 )
在第一个for循环执行结束之后,$v 指向的 $arr[2] 的引用,在执行第二个for循环的时候,第一个循环开始是,将 $arr[0] 赋值给 $v,就相当于将 $arr[0] 赋值给 $arr[2],第二次循环是将 $arr[1] 辅助给 $arr[2],第三次赋值是将 $arr[2] 赋值给 $arr[2],赋值给自身,每次循环打印出来的话如下所示:
<?php
$arr = [1, 2, 3];
foreach ($arr as &$v) {
// no enything
}
foreach ($arr as $k => $v) {
echo '第'.(++$k).'次循环:';
print_r($arr);
}
// echo 第1次循环: Array ( [0] => 1 [1] => 2 [2] => 1 )
// echo 第2次循环: Array ( [0] => 1 [1] => 2 [2] => 2 )
// echo 第3次循环: Array ( [0] => 1 [1] => 2 [2] => 2 )
这个挺好理解的,将循环拆解之后便一目了然,再来看下面这道:
<?php
$arr = [1, 2, 3];
foreach ($arr as $k => $v) {
$v = &$arr[$k];
}
print_r($arr); //echo Array ( [0] => 2 [1] => 3 [2] => 3 )
怎么样,和你想的一样么,这个比上面的那道更难一些,是将上面的两个循环结合在一起,跟复杂一些,在第一次循环结束时,$v 指向 $arr[0],第二次循环时开始时,$arr[1] 赋值给 $v 相当于赋值给 $arr[0],第二次循环结束时 $v 指向 $arr[1],第三次循环开始时将 $arr[2] 赋值给 $v 相当于赋值给了 $arr[1],第三次循环什么都没做,简单来说就是每次循环将第 (i) 个元素的值赋值到 (i-1) 的位置上,打印输出一下:
<?php
$arr = [1, 2, 3];
foreach ($arr as $k => $v) {
$v = &$arr[$k];
echo '第'.(++$k).'次循环:';
print_r($arr);
}
// echo 第1次循环: Array ( [0] => 1 [1] => 2 [2] => 3 )
// echo 第2次循环: Array ( [0] => 2 [1] => 2 [2] => 3 )
// echo 第3次循环: Array ( [0] => 2 [1] => 3 [2] => 3 )
这两道题咋一看之下有点懵,但是细想之下是考察对于循环和引用的理解,越是细节的东西越要在意,越是基础的东西越要理解,基础打好了才能进步的更快啊。