文章圍繞“JavaScript 中的‘return’是什么意思”展開,先解釋有無‘return’的函數區別,闡述函數的可重用性和可維護性,接著探討為何需要‘return’,分析多種替代方案的不可行性,最后總結在 JavaScript 中因規范和多種因素必須使用‘return’。
最近朋友問了我一個問題:“JavaScript 中的 return
是什么意思?”
jsx代碼解讀復制代碼
function contains(px, py, x, y) {
const d = dist(px, py, x, y);
if (d > 20) return true; // 這行是什么意思?
else return false; // 那這一行呢?
}
一開始我覺得這個問題很簡單,但它背后其實蘊藏了一些重要且有趣的概念!
因為我朋友是藝術背景,所以本篇文章的結論是一些很基礎的東西,大家感興趣可以繼續看下去。
兩種函數
我先解釋了有 return
和沒有 return
的函數的區別。函數是一組指令,如果你需要這組指令的執行結果,就需要一個 return
語句,否則不需要。
例如,要獲得兩個數的和,你應該聲明一個帶有 return
語句的 add 函數:
jsx代碼解讀復制代碼
function add(x, y) {
return x + y; // 帶有 return 語句
}
然后你可以這樣使用 add 函數:
jsx代碼解讀復制代碼const a = 1; const b = 2; const c = add(a, b); // 3 const d = add(b, c); // 5
如果你只是想在控制臺打印一條消息,則不需要在函數中使用 return
語句:
jsx代碼解讀復制代碼function great(name) { console.log(`Hello ${name}!`); }
你可以這樣使用 great 函數:
jsx代碼解讀復制代碼great('Rachel');
我原以為我已經解答了朋友的問題,但她又提出了一個新問題:“為什么我們需要這個求和函數?我們可以在任何地方寫 a + b
,那為什么還要用 return
語句?”
jsx代碼解讀復制代碼const a = 1; const b = 2; const c = a + b; // 3 const d = b + c; // 5
此時,我意識到她的真正問題是:“我們為什么需要函數?”
為什么需要函數?
為什么要使用函數?盡管有經驗的程序員有無數的理由,這里我只關注一些與我朋友問題相關的原因
可重用的代碼
她的確有道理。我們可以輕松地在任何地方寫 a + b
。然而,這僅僅因為加法是一個簡單的操作。如果你想執行一個更復雜的計算呢?
jsx代碼解讀復制代碼const a = 1; const b = 2; // 這是否易于在每個地方寫? const c = 0.6 + 0.2 * Math.cos(a * 6.0 + Math.cos(d * 8.0 + b));
如果你需要多個語句來獲得結果呢?
jsx代碼解讀復制代碼const a = 1; const b = 2; // t 是一個臨時變量 const t = 0.6 + 0.2 * Math.cos(a * 6.0 + Math.cos(d * 8.0 + b)); const c = t ** 2;
在這兩種情況下,重復編寫這些代碼會很麻煩。對于這種可重用的代碼,你可以將其封裝在一個函數中,這樣每次需要它時就不必重新實現了!
jsx代碼解讀復制代碼function theta(a, b) { return 0.6 + 0.2 * Math.cos(a * 6.0 + Math.cos(d * 8.0 + b)); } const a = 1; const b = 2; const c = theta(a, b); const d = theta(b, c);
易于維護
在討論可重用性時,你無法忽視可維護性。唯一不變的是世界總是在變化,這對于代碼也一樣!你的代碼越容易修改,它就越具可維護性。
如果你想在計算結果時將 0.6
改為 0.8
,沒有函數的情況下,你必須在每個執行計算的地方進行更改。但如果有一個函數,你只需更改一個地方:函數內部!
jsx代碼解讀復制代碼function theta(a, b) { // 將 0.6 更改為 0.8,你就完成了! return 0.8 + 0.2 * Math.cos(a * 6.0 + Math.cos(d * 8.0 + b)); }
毫無疑問,函數增強了代碼的可維護性。就在我以為我解答了她的問題時,她又提出了另一個問題:“我理解了函數的必要性,但為什么我們需要寫 return
?”
為什么需要 return
?
真有意思!我之前沒有考慮過這個問題!她隨后提出了一些關于 return
的替代方案,這些想法非常有創意!
為什么不直接返回最后一條語句?
第一個建議的方案是“為什么不直接返回最后一條語句?”
jsx代碼解讀復制代碼function add(a, b) { a + b } const sum = add(1, 2); // undefined
我們知道,在 JavaScript、Java、C 或許多其他語言中,這樣是不允許的。這些語言的規范要求顯式的 return
語句。然而,在某些語言中,例如 Rust,這是允許的:
rust代碼解讀復制代碼fn add(a: i32, b: i32) -> i32 { a + b } let sum = add(1, 2); // 3
然而值得注意的是,JavaScript 中的另一種函數類型不需要 return
語句!那就是帶有單個表達式的箭頭函數:
sx代碼解讀復制代碼const add = (x, y) => x + y; const sum = add(1, 2); // 3
如果我們將結果賦值給局部變量呢?
然后她提出了另一個有創意的解決方案:“如果我們將結果賦值給一個局部變量呢?”
jsx代碼解讀復制代碼function add(x, y) { let sum = x + y; } add(1, 2); sum; // Uncaught ReferenceError: sum is not defined
她很快注意到我們無法訪問 sum
變量。這是因為使用 let
關鍵字聲明的變量只在其定義的作用域內可見——在這個例子中是函數作用域。
可以將函數視為黑盒子。你將參數放入盒子中,期待獲得一個輸出(返回值)。只有返回值對外部世界(父作用域)是可見的(或可訪問的)。
將結果賦值給全局變量呢?
如果我們在函數作用域之外訪問這個值呢?將其賦值給一個全局變量怎么樣?
jsx代碼解讀復制代碼let sum; function add(x, y) { sum = x + y; } add(1, 2); sum; // 3
啊,修改全局變量!副作用!非純函數!這些想法在我腦海中浮現。但我如何在一分鐘內解釋為什么這是一個糟糕的選擇呢?
避免這種方法的一個關鍵原因是,別人很難知道具體的全局變量是在哪個函數中被修改的。他們需要去查找結果在哪兒,而不是直接從函數中獲取!
總結
簡而言之,我們需要 return
,因為我們需要函數,而在 JavaScript 中的標準函數中沒有可行的替代方案。
函數的存在是為了使代碼具有可重用性和可維護性。由于 JavaScript 的規范、函數作用域的限制以及修改全局變量帶來的風險,我們在 JavaScript 的標準函數中必須使用 return
語句。
作者:小小酥梨
鏈接:https://juejin.cn/post/7434460436307591177
該文章在 2024/11/18 18:00:18 編輯過