寫在前面
閒來無事在瀏覽Laravel 底層分析這篇文章的時候,發現自己對匿名函式只是停留在表面上的理解,然鵝,作為一名“優秀”的??,發自內心的好奇心讓我去好好把玩了下匿名函式這個東西。
匿名函式
匿名函式(Anonymous functions),也叫閉包函式(closures),允許 臨時建立一個沒有指定名稱的函式。最經常用作回撥函式(callback)引數的值,也可以作為函式的返回值。
示例1:
//宣告一個匿名函式,並呼叫
<?php
$say = function() {
echo 'hello'; //宣告一個匿名函式,並賦值給$say這個變數,注意函式宣告後面的‘;’
};
$say(); //變數加上()就可以呼叫匿名函式,輸出“hello”
示例2:
//宣告一個匿名函式,並作為回撥函式(callback)引數的值
$say = function($name) {
echo 'hello '.$name;
};
call_user_func($say,'dog'); //call_user_func呼叫引數1的回撥函式,並將引數2當成回撥函式的引數,輸出“hello dog”
匿名函式 和 閉包類(Closure)的關係
定義一個閉包函式,會產生一個閉包類(Closure)的物件。
Closure 類主要方法:
Closure {
/* 方法 */
__construct ( void )
public static bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] ) : Closure
public bindTo ( object $newthis [, mixed $newscope = 'static' ] ) : Closure
}
-
Closure::construct — 用於禁止例項化的建構函式
這個方法僅用於禁止例項化一個 Closure 類的物件。
-
Closure::bind — 用來複制引數1給定的閉包,並指定閉包的 this 指標指向引數2,閉包的類的作用域為 引數3
引數
- closure
需要繫結的匿名函式。 - newthis
需要繫結到匿名函式的物件,或者 NULL 建立未繫結的閉包。 - newscope
想要繫結給閉包的類作用域,或者 'static' 表示不改變。如果傳入一個物件,則使用這個物件的型別名。 類作用域用來決定在閉包中 $this 物件的 私有、保護方法 的可見性
- closure
-
Closure::bindTo — 複製當前閉包物件,繫結指定的$this物件和類作用域。
用法和bind差不多,這裡就不多贅述
示例1:
//宣告一個狗物件
class Dog
{
public $name = '旺財';
}
//宣告一個貓物件
class Cat
{
public $name = '喵喵';
}
//宣告一個匿名函式,用來輸出名字
$sayName = function() {
echo $this->name;
};
//用bind賦值上面那個匿名函式,並指定this指向狗物件,返回新的閉包函式
$sayDogName = Closure::bind($sayName, new Dog);
//呼叫新的閉包函式,輸出 “旺財”
$sayDogName();
//用bind賦值上面那個匿名函式,並指定this指向貓物件,返回新的閉包函式
$sayCatName = Closure::bind($sayName, new Cat);
//呼叫新的閉包函式,輸出 “喵喵”
$sayCatName();
//用bind賦值上面那個匿名函式,this指向null
$sayNullName = Closure::bind($sayName, null);
//呼叫新的閉包函式,由於this指向null,報錯 “Using $this when not in object context”
$sayNullName();
上面這個例子闡明瞭引數2的作用,那麼引數3呢,往下瞅
示例2:
//還是宣告一條狗狗,不過狗狗的名字變成了私有,只有主人才知道狗狗叫旺財
class Dog
{
private $name = '旺財';
}
//宣告匿名函式
$sayName = function() {
echo $this->name;
};
//用bind賦值上面那個匿名函式,並指定this指向狗物件,返回新的閉包函式
$sayDogName = Closure::bind($sayName, new Dog);
//呼叫新的閉包函式,報錯 “Cannot access private property Dog::$name”
$sayDogName();
原因:
我們將 this 指向了 Dog 物件,但是我們並沒有把類的作用域指向 Dog,所以,閉包函式 sayDogName 無法呼叫 Dog 的私有屬性 name。接下來,我們來指定下類的作用域。
示例3:
//指定類的作用域為 Dog 物件
$sayDogName = Closure::bind($sayName, new Dog, 'Dog');
//輸出 “旺財”
$sayDogName();
匿名函式 和 Use
use是用來是連線閉包和外界變數。
示例1:
$a = 1;
$b = function () use($a) {
$a += 10;
echo $a.PHP_EOL; //輸出“11”
};
$b();
echo $a; //輸出1
那麼如果我們傳遞的變數是個物件呢?
class Dog
{
public $name = '旺財';
}
$aDog = new Dog;
$closureFunc = function() use($aDog) {
$aDog->name = '旺財他二爺';
};
$closureFunc();
echo $aDog->name; //輸出“旺財他二爺”
咦,不是說閉包函式裡面的操作不會改變閉包函式外的變數嗎?咋回事??
其實,物件變數屬於引用型別的資料,use在複製物件變數的時候,雖然操作的是另外一個變數,但是這個變數其指向的物件地址並沒有發生改變,所以當我們對複製的物件變數進行操作的時候,物件的屬性也會發生相應的改變,同時會影響到其他也指向這個物件的變數。
舉個簡單?做下對比:
$a = 1;
$b = $a;
$b = 2;
echo $a; //輸出1
class Dog
{
public $name = '旺財';
}
$a = new Dog;
$b = $a;
$b->name = '旺財的兒子';
echo $a->name; //輸出“旺財的兒子”
同理,如果我們需要同時改變閉包外普通變數$a的值,那麼我們就需要引用傳值&,如下:
$a = 1;
$b = function () use(&$a) {
$a += 10;
echo $a.PHP_EOL; //輸出“11”
};
$b();
echo $a; //輸出11
最後關於 Use 傳值的一點小疑問
匿名函式也可以通過函式的引數傳遞變數進來,如下
$a = 1;
$b = function ($a) {
$a += 10;
echo $a.PHP_EOL; //輸出“11”
};
$b($a);
echo $a; //輸出1
那麼這種傳值方式和Use有何區別?希望看到的小夥伴可以在評論區幫小弟解答一下,感激不盡!!