與匿名函數不同,箭頭函數不能有 void 返回類型聲明。
這看起來可能很明顯,但如果您認為您可以利用箭頭函數的優點(使用父作用域中的變數)來簡化函數或方法調用,請記住,只有在您*沒有*告訴 PHP 箭頭函數確實返回 void 時,這才有可能。
箭頭函式在 PHP 7.4 中引入,作為匿名函式的更簡潔語法。
匿名函式和箭頭函式都是使用 Closure 類別實作的。
箭頭函式的基本形式為 fn (argument_list) => expr
。
箭頭函式支援與匿名函式相同的功能,差別在於使用父作用域中的變數一律為自動的。
當表達式中使用的變數在父作用域中定義時,它會被隱式地以值捕獲。在以下範例中,函式 $fn1 和 $fn2 的行為方式相同。
範例 #1 箭頭函式自動以值捕獲變數
<?php
$y = 1;
$fn1 = fn($x) => $x + $y;
// 等同於以值使用 $y:
$fn2 = function ($x) use ($y) {
return $x + $y;
};
var_export($fn1(3));
?>
上述範例將輸出
4
即使箭頭函式是巢狀的,這也同樣有效
範例 #2 箭頭函式自動以值捕獲變數,即使是巢狀的也一樣
<?php
$z = 1;
$fn = fn($x) => fn($y) => $x * $y + $z;
// 輸出 51
var_export($fn(5)(10));
?>
與匿名函式類似,箭頭函式語法允許任意函式簽名,包括參數和返回類型、預設值、可變參數,以及透過引用傳遞和返回。以下所有範例都是有效的箭頭函式
範例 #3 箭頭函式範例
<?php
fn(array $x) => $x;
static fn($x): int => $x;
fn($x = 42) => $x;
fn(&$x) => $x;
fn&($x) => $x;
fn($x, ...$rest) => $rest;
?>
箭頭函數使用按值綁定變數。這大致相當於對箭頭函數內使用的每個變數 $x 執行 use($x)
。按值綁定意味著無法修改外部作用域中的任何值。可以使用匿名函數來進行按引用綁定。
範例 #4:箭頭函數無法修改外部作用域的值
<?php
$x = 1;
$fn = fn() => $x++; // 無作用
$fn();
var_export($x); // 輸出 1
?>
版本 | 說明 |
---|---|
7.4.0 | 箭頭函數開始可用。 |
注意:可以在箭頭函數內使用 func_num_args()、func_get_arg() 和 func_get_args()。
與匿名函數不同,箭頭函數不能有 void 返回類型聲明。
這看起來可能很明顯,但如果您認為您可以利用箭頭函數的優點(使用父作用域中的變數)來簡化函數或方法調用,請記住,只有在您*沒有*告訴 PHP 箭頭函數確實返回 void 時,這才有可能。
在範例 4 中(箭頭函數無法修改外部作用域的值)
<?php
$x = 1;
$fn = fn() => $x++; // 無作用
$fn();
var_export($x); // 輸出 1
?>
在這裡,我們可以在 fn(&$x) 中使用引用變數,並從函數調用 $fn($x) 傳遞值,這樣我們就可以在不使用匿名函數的情況下獲得預期的輸出。
範例
<?php
$x = 1;
$fn = fn(&$x) => $x++;
$fn($x);
var_export($x);
?>
輸出:2(如預期)
但在這裡,它不會自動從父作用域取值,而是必須明確傳遞。
如您所知,箭頭函式中的變數綁定是透過「按值」方式進行的。這表示箭頭函式會傳回外部作用域中所使用變數值的一個副本。
現在讓我們來看一個箭頭函式如何傳回參考而不是值的副本的範例。
<?php
$x = 0;
$fn = fn &(&$x) => $x; // 傳回一個參考
$y = &$fn($x); // 現在 $y 代表該參考
var_dump($y); // 輸出:0
$y = 3; // 改變 $y 的值會影響 $x
var_dump($x); // 輸出:3
?>
注意 compact() 無法從外部作用域存取(導入)變數(已知版本:7.4.0、7.4.8)(錯誤:https://bugs.php.net/bug.php?id=78970)。
有一個可用的解決方法 - 直接使用變數;這將導致它被導入到箭頭函式的命名空間中,並使其可供 compact() 使用。
<?php
$aa = 111;
$accessing_variable_works = fn($bb) => [ $aa, $bb ];
$compact_is_broken = fn($bb) => compact('aa', 'bb');
$compact_can_work_with_workaround = fn($bb) => compact('aa', 'bb') + ['workaround' => $aa];
var_dump($accessing_variable_works(333));
var_dump($compact_is_broken(555));
var_dump($compact_can_work_with_workaround(777));
?>
結果
陣列(2) {
[0]=>
整數(111)
[1]=>
整數(333)
}
PHP 提醒: compact(): 未定義的變數: aa 於 /home/m/vlt/guitar/tlb/s/public_html/index.php 第 9 行
陣列(1) {
["bb"]=>
整數(555)
}
陣列(3) {
["aa"]=>
整數(111)
["bb"]=>
整數(777)
["workaround"]=>
整數(111)
}
如果您是 JavaScript 開發人員,以下列出與 JS 箭頭函式(Arrow Function)的相似和相異之處
相同之處
- 建立匿名函式
- 將 "$this" 的值綁定到父作用域中的值。
- (以及父作用域中的所有其他變數。請參閱以下說明)
相異之處
- 您必須寫 "fn()" 而不是只有 "()"
- 函式主體僅限於一個表達式
- 因此沒有使用 "{" 和 "}" 的多行函式主體
同時相同又相異之處
- 綁定父作用域中的所有變數
- 在 JavaScript 中,所有函式都是閉包(Closure),綁定到父作用域中的變數("this" 除外)。
- 但在 PHP 中,使用 "function() {}" 定義的普通匿名函式無法存取父作用域,除非它們使用關鍵字 "use" 明確宣告閉包
- 另一方面,PHP 箭頭函式會自動綁定到父作用域中的所有變數。因此,這使得它們的行為與 JS 函式相同,但請注意,在 PHP 中,這是箭頭函式獨有的特殊行為。