没有花括号的箭头函数

120

我对ES6和React都很陌生,但一直看到箭头函数。为什么有些箭头函数在瘦箭头后使用花括号,而有些则使用括号?例如:

const foo = (params) => (
    <span>
        <p>Content</p>
    </span>
);

对比。

const handleBar = (e) => {
    e.preventDefault();
    dispatch('logout');
};

请参阅箭头函数中的花括号 - Bergi
9个回答

197

圆括号返回单个值,花括号执行多行代码。

您的示例看起来很混乱,因为它使用JSX,看起来像多个“行”,但实际上只编译为单个“元素”。

以下是一些做同样事情的更多示例:

const a = (who) => "hello " + who + "!";
const b = (who) => ("hello " + who + "!");
const c = (who) => (
  "hello " + who + "!"
);
const d = (who) => (
  "hello "
    + who
    + "!"
);
const e = (who) => {
  return "hello " + who + "!";
}; 

你也经常会看到对象字面量周围有括号,因为这是避免解析器将其视为代码块的一种方式:

const x = () => {} // Does nothing
const y = () => ({}) // returns an object

3
太棒了,谢谢。这也帮助我理解了一些其他错误信息。一旦我能够确认无误,我将接受它是正确的。谢谢David。 - dkimot
3
可以使用花括号来防止箭头函数返回值,或者让单行箭头函数不返回任何内容更加明显。请参考我的答案示例(无法将其格式化为注释)。 - GrayedFox
2
我理解GrayedFox的想法,但是为什么有人要实现这个呢?对我来说似乎有点棘手,因为在某些特殊情况下,你可能不确定应该使用()还是{}。 - Advena
我能否只使用花括号而不是圆括号来使用“return”?如果可以,为什么? - vikramvi
1
@vikramvi 因为圆括号意味着函数将返回它们内部单个语句的结果,即 const x = () => (x) 等同于 const x = () => {return x}。你总是可以使用花括号,但是你可以使用圆括号来简洁地从单个语句中返回一个值。 - Alan Evangelista
@AlanEvangelista 感谢您的澄清。JS 既灵活又令人困惑;在 Java 中,我们有严格的做事方式,避免这种混淆 :) - vikramvi

39

一个人也可以使用花括号来防止单行箭头函数返回值,或者让下一个开发者明显知道在这种情况下,单行箭头函数不应该返回任何东西。

例如:

const myFunc = (stuff) => { someArray.push(stuff) }
const otherFunc = (stuff) => someArray.push(stuff)

console.log(myFunc())    // --> logs undefined
console.log(otherFunc()) // --> logs result of push which is new array length

这是一个很好的例子,在我之前被绊倒了,我想知道为什么我的数组没有被push更新,当在myFunc中的someArray之前缺少return时。 - Rin and Len

18

括号在箭头函数中用于返回一个对象。

() => ({ name: 'YourName' })  // This will return an object

这相当于

() => {
   return { name : 'YourName' }
}

14

实际上,当有人在箭头函数声明中使用括号时,它等同于以下内容:

const arrow = number => number + 1;

|||

const arrow = (number) => number + 1;

|||    

const arrow = (number) => ( number + 1 );

|||

const arrow = (number) => { return number + 1 };

我能否只使用大括号而不是圆括号来使用 "return" ?如果可以,为什么? - vikramvi
@vikramvi,看,这只是一个简单的语法,当你的函数在执行上下文中没有任何内容时,只需用更少的代码使其简单化,=>不带花括号表示return,易于阅读,易于理解,包大小更小。看,它是纯粹的美。 - AmerllicA
谢谢您提供的信息,我明白了;但是我的问题是:是否可以同时使用“return”和()? - vikramvi
1
@vikramvi,显然不行。 - AmerllicA

10
括号具有隐式返回语句,而大括号则需要显式的返回语句。

6
如果您在箭头后使用花括号定义函数体,则必须使用 "return" 关键字返回某些内容。
例如:
const myFun1 = (x) => {
    return x;
}; // It will return x

const myFun2 = (x) => {
    x;
}; // It will return nothing

如果你使用括号,就不需要提及'return'关键字。

例如:

const myFunc1 = (x) => x; // It will return x

const myFunc2 = (x) => (x); // It will also return x

2
在你的第一个示例中,箭头函数的右侧显示了一个由分组运算符括起来的单个表达式:
const foo = (params) => (
  <span>
    <p>Content</p>
  </span>
);

一个类似的可比较的案例是:
const foo = (params) => (<span><p>Content</p></span>);

在上述情况下,使用单个表达式时的区别在于右侧是函数的返回值
另一方面,如果您使用花括号,JavaScript 将理解为语句:
const foo = (params) => {} // this is not an object being returned, it's just an empty statement 

因此,使用“using”语句是您在其中编写代码的良好起点,可以有多行,并且如果函数需要返回值,则需要使用“return”:
const foo = (params) => {
    let value = 1; 
    return value;
}

如果您想以最简短的形式返回一个空对象:
const foo = (params) => ({}) 

查看测试


0

每个函数都有两个方面。

它们中的第一个是,每个函数(不仅仅是箭头函数)都有一个执行上下文(块作用域),在其中创建和使用变量。

换句话说,在函数的花括号 { ... } 内部声明和分配的内容会留在那里,并且对外部函数/变量不可见。

例如,当编写以下内容时:

let x = 100;

function doSomething() {
  let x = 50;
  console.log(x);
}

doSomething();     // 50
console.log(x);    // 100

两个值都在控制台中显示(而不是“外部的x只是被内部函数的x替换”)。

尽管 let 通常不允许再次声明其他变量 x(具有相同名称 x),但在这种情况下,因为第二个 x 在 { ... } 中声明和初始化,它不会改变外部的 x,这也是因为在调用函数 doSomething 后,其中的x被创建、分配、打印到控制台,然后被销毁(从内存中删除)。所以每次我们通过运行 doSomething() 调用该函数时,这个过程都会发生。

因此,在理解函数时需要考虑的第一个方面就是:它们执行完后会忘记代码内部花括号中创建的变量值。

正因为如此,更容易理解它们的第二个方面——函数不能仅仅孤立地工作,它们还需要向其他函数发送数据,因此它们具有一些“报告方面”,用于外部化其花括号内计算结果的某些部分,这正是 return 语句存在的原因。

每个函数中都存在return,即使在console.log或alert()中,甚至在doSomething()中也是如此。但在这些情况下,我们没有明确地为其设置任何内容,它总是'return undefined'。

因此,写它并不是必要的,而是要知道在您没有返回特定内容的情况下,函数本身将通过返回undefined来为您完成。

当您编写(或使用)一个仅用于执行某些操作的函数时,它也将始终返回undefined。

您可以通过每个函数来检查该函数是否具有声明的返回值:
let x = alert(100);
console.log(x); // undefined

let y = doSomething(); // console prints 50
console.log(y);        // 50, then undefined --- 2 lines

console.log(alert('Hello')); // undefined

console.log(console.log('Okay')); // Okay , then undefined

为什么会这样呢?

因为alert()是全局对象window的方法(在浏览器中),所以实际上是window.alert(),console.log()也是一样的(也就是window.console.log()),它们执行某些操作(将括号中的内容打印到警告框或控制台中),然后返回undefined。

现在回到箭头函数,它们不仅是编写函数的新符号表示法,还具有一些特定的功能。

首先,在箭头函数的括号中只有一个参数时,可以省略括号。

其次,如果花括号中只有一个语句,则也可以省略花括号。

第三个是,如果单个语句是一个return语句,则可以省略return关键字。

通过使用这些方法,我们可以将许多常规函数转换为箭头函数(如果需要):

function doSomething() {let x = 50; console.log(x);} // as function declaration

let doSomething = function() {let x = 50; console.log(x);}; // as function expression, which is an anonymous function assigned to the variable 'doSomething'

let doSomething = () => {let x = 50; console.log(x);}; // as arrow function

// let's transform it further

let doSomething = () => {console.log(50)}; //

// that is equivalent to ---- let doSomething = () => {console.log(50); return undefined};
// or even to           ---- let doSomething = () => {return ( console.log(50) ) };
// because anyways, *console.log* has *return undefined* in it, as explained above

//which is the same as  ---- let doSomething = () => {return console.log(50) };

// let's now apply the rules 2 and 3 from above, one by one:

let doSomething = () => return console.log(50);

let doSomething = () => console.log(50);

// Obviously, this just shows how we could rewrite many usual functions (functions declarations) into arrow functions
// we can do that safely if we don't have any **this** involved in the functions, of course
// also, from all lines of code above only one must remain, for example the last one.

// the last one, despite only having ---- console.log(50) --- as the execution aspect, it also ---- returns undefined ---- as well

// obviously ---- console.log( typeof doSomething );   // function
// while     ---- console.log( typeof doSomething() ); // undefined

如果箭头函数有两个或更多参数,我们不能省略它们周围的括号:

function sum(a, b) {let total = a + b; return total}

let sum = function(a, b) {let total = a + b; return total};
// or
let sum = (a, b) => {let total = a + b; return total};
// or
let sum = (a, b) => {return a + b};
// or
let sum = (a, b) => a + b;

对于上述简单的操作,胖箭头符号“=>”可以被“读作”被转换为,换句话说,a和b 被转换为 a + b。

相反地,也有一些函数用于验证某些数据(例如检查数据类型等),像这样的函数:

let isNumber = x => typeof x === "number";
// or
let isNumber = (x) => {return (typeof x === "number")};
// obviously, 
isNumber("Hello, John!"); // false

这些不会转换数据,因此箭头符号可以被理解为更多的是“在条件下”,或类似的意思。

换句话说,像这样的函数

let double = x => x * 2    // 'double' is a function that transforms x into x*2

不同于检查函数(主要用于过滤器、排序和其他类型的验证函数,通常作为回调函数等)

let isArray = arr => Array.isArray(arr) // that last one already returns boolean by itself, no need to write return (Array.isArray() etc)

最后需要了解的关于 return 的一件事是,当你在多行中编写代码时,ASI(自动分号插入)会在你误按回车键后自动在 return 之后插入一个 ';',这将导致代码出错。因此,与其写成:
return
a+b;

你的代码将会表现为

return;
a+b;

所以最好写代码时加上括号,就像这样:

return (
  a + b
);

正如MDN网站此处所解释的那样。


0
为了回答一个重复的帖子(在这里发布的问题),只是为了给其他人参考:
  var func = x => x * x;                  
    // concise body syntax, implied "return"

    var func = (x, y) => { return x + y; }; 
    // with block body, explicit "return" needed

参考资料:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Function_body

另外请注意: 如果你在一个箭头函数中返回一个对象字面量作为结果,那么你必须将对象括在括号中,例如:myFunc = () => ({ data: "hello"})。如果省略括号,你将会收到一个错误,因为构建工具会认为对象字面量的花括号是函数体的开始和结束。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接