JavaScript 中的 ‘return’ 是什么意思?
最近朋友问了我一个问题:“JavaScript 中的 return
是什么意思?”
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 函数:
function add(x, y) {
return x + y; // 带有 return 语句
}
然后你可以这样使用 add 函数:
const a = 1;
const b = 2;
const c = add(a, b); // 3
const d = add(b, c); // 5
如果你只是想在控制台打印一条消息,则不需要在函数中使用 return
语句:
function great(name) {
console.log(`Hello ${name}!`);
}
你可以这样使用 great 函数:
great('Rachel');
我原以为我已经解答了朋友的问题,但她又提出了一个新问题:“为什么我们需要这个求和函数?我们可以在任何地方写 a + b
,那为什么还要用 return
语句?”
const a = 1;
const b = 2;
const c = a + b; // 3
const d = b + c; // 5
此时,我意识到她的真正问题是:“我们为什么需要函数?”
为什么需要函数?
为什么要使用函数?尽管有经验的程序员有无数的理由,这里我只关注一些与我朋友问题相关的原因
可重用的代码
她的确有道理。我们可以轻松地在任何地方写 a + b
。然而,这仅仅因为加法是一个简单的操作。如果你想执行一个更复杂的计算呢?
const a = 1;
const b = 2;
// 这是否易于在每个地方写?
const c = 0.6 + 0.2 * Math.cos(a * 6.0 + Math.cos(d * 8.0 + b));
如果你需要多个语句来获得结果呢?
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;
在这两种情况下,重复编写这些代码会很麻烦。对于这种可重用的代码,你可以将其封装在一个函数中,这样每次需要它时就不必重新实现了!
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
,没有函数的情况下,你必须在每个执行计算的地方进行更改。但如果有一个函数,你只需更改一个地方:函数内部!
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
的替代方案,这些想法非常有创意!
为什么不直接返回最后一条语句?
第一个建议的方案是“为什么不直接返回最后一条语句?”
function add(a, b) {
a + b
}
const sum = add(1, 2); // undefined
我们知道,在 JavaScript、Java、C 或许多其他语言中,这样是不允许的。这些语言的规范要求显式的 return
语句。然而,在某些语言中,例如 Rust,这是允许的:
fn add(a: i32, b: i32) -> i32 {
a + b
}
let sum = add(1, 2); // 3
然而值得注意的是,JavaScript 中的另一种函数类型不需要 return
语句!那就是带有单个表达式的箭头函数:
const add = (x, y) => x + y;
const sum = add(1, 2); // 3
如果我们将结果赋值给局部变量呢?
然后她提出了另一个有创意的解决方案:“如果我们将结果赋值给一个局部变量呢?”
function add(x, y) {
let sum = x + y;
}
add(1, 2);
sum; // Uncaught ReferenceError: sum is not defined
她很快注意到我们无法访问 sum
变量。这是因为使用 let
关键字声明的变量只在其定义的作用域内可见——在这个例子中是函数作用域。
可以将函数视为黑盒子。你将参数放入盒子中,期待获得一个输出(返回值)。只有返回值对外部世界(父作用域)是可见的(或可访问的)。
将结果赋值给全局变量呢?
如果我们在函数作用域之外访问这个值呢?将其赋值给一个全局变量怎么样?
let sum;
function add(x, y) {
sum = x + y;
}
add(1, 2);
sum; // 3
啊,修改全局变量!副作用!非纯函数!这些想法在我脑海中浮现。但我如何在一分钟内解释为什么这是一个糟糕的选择呢?
避免这种方法的一个关键原因是,别人很难知道具体的全局变量是在哪个函数中被修改的。他们需要去查找结果在哪儿,而不是直接从函数中获取!
总结
简而言之,我们需要 return
,因为我们需要函数,而在 JavaScript 中的标准函数中没有可行的替代方案。
函数的存在是为了使代码具有可重用性和可维护性。由于 JavaScript 的规范、函数作用域的限制以及修改全局变量带来的风险,我们在 JavaScript 的标准函数中必须使用 return
语句。
这次讨论非常有趣!我从未想过看似简单的问题背后竟然蕴含着如此多的有趣的思考。与不同视角的人交流总能带来新的见解!
来源:juejin.cn/post/7434460436307591177