【问题标题】:What type is the 'return' keyword?“return”关键字是什么类型的?
【发布时间】:2016-08-09 23:56:38
【问题描述】:

我们在 JavaScript 函数中可选地使用 return 语句。这是一个关键字。但是return 本身的实际类型是什么。其实看到这个例子我就糊涂了:

function add(a, b) {
  return (
    console.log(a + b),
    console.log(arguments)
  );
}

add(2, 2);

输出:

4
[2, 2]

因此,我们可以将逗号分隔的表达式传递到 return 语句中。这是一个函数吗?

从这里开始,我们可以大胆猜测 JavaScript 中的每个关键字最终都是一个函数吗?

我写了一个小博客作为讨论的要点。你可能想检查一下here

【问题讨论】:

  • 就在你以为你理解 JavaScript 的时候,出现了这样的事情......
  • 括号不代表你在调用函数,例如:var a = (2 + 2).
  • 一个更合适的例子是var a = (1, 2)——这将导致a的值2由于逗号运算符的操作方式(是的,逗号是一个数学运算符,如+| 等,具体来说,在拉姆巴达演算中,逗号运算符实现了 K 组合子)
  • 那么正确的做法是揭穿误解,而不是投反对票。
  • @FlorianPeschka 不是,这是任何 C 风格语言的标准行为。

标签: javascript return keyword


【解决方案1】:

但是“返回”本身的实际类型是什么。

它没有类型,也不是值。

尝试typeof return; 会得到Unexpected token return

因此,我们可以将逗号分隔的表达式传递到 return 语句中。这是一个函数吗?

不,虽然括号可以用于调用函数,但这里它们是一个grouping operator,其中包含由comma operator分隔的几个表达式。

更有用的演示是:

function add(a, b) {
  return (
    (a + b),
    (a - b)
  );
}

console.log(add(2, 2));

哪个输出0,因为a + b 的结果被忽略(它在逗号运算符的LHS 上)并返回a - b

【讨论】:

  • 但是,在上面的例子中,每个表达式都被一个一个地执行。我认为您所说的情况与原始问题中所说的情况不同。我想说的是Group运算符的优先级在哪里应用。
  • @ArnabDas — 是的,它们已被执行,但 return 看不到它们,这就是你要问的。
  • @ArnabDas — “我想说的是组运算符的优先级在哪里应用。” — 当然是在分组运算符中。
  • @ArnabDas — 是的。 a + b 只是没有可见的效果,因为它没有副作用并且该值被丢弃(好吧,鉴于它没有实际效果,优化编译器可能会删除它,但这没有实际区别)。
  • @Roman 是正确的。 “跳过”表达式使其不被执行的唯一方法(除了在像if 这样的条件内)是短路评估。逗号运算符不会短路,因此它会评估双方。然后它会返回最后一个。这就是逗号运算符的明确目的,将表达式分组到单个语句中(通常忽略整个语句的结果值),最常用于变量初始化:var i = 2, j = 3, k = 4;
【解决方案2】:

测试返回带括号的值时会发生什么:

function foo() {
    return (1, 2);
}

console.log(foo());

给出答案2,因此逗号分隔的值列表的计算结果似乎是列表中的最后一个元素。

真的,括号在这里无关紧要,它们是对操作进行分组而不是表示函数调用。然而,令人惊讶的是,逗号在这里是合法的。我在这里找到了一篇关于如何处理逗号的有趣博客文章:

https://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

【讨论】:

  • 我想,像return ([1, 2]) 这样的东西应该打印两个值?
  • 那将返回一个数组。
  • console.log((1, 2))
【解决方案3】:

return 不是函数。它是它发生的函数的延续

想想alert (2 * foo(bar)); 语句,其中foo 是函数的名称。当您评估它时,您会发现您必须暂时搁置声明的其余部分以专注于评估foo(bar)。您可以将您留出的部分想象成alert (2 * _) 之类的东西,并用一个空白来填写。当您知道foo(bar) 的值是什么时,您可以再次选择它。

您搁置的是电话foo(bar)继续

调用return 会为该延续提供一个值。

当您评估foo 内的函数 时,foo 的其余部分会等待该函数减少到一个值,然后foo 会再次启动。你还有一个评估foo(bar) 的目标,它只是暂停了。

当您在 foo 中评估 return 时,foo 的任何部分都不会等待值。 return 不会减少到您使用它的foo 内部的值。相反,它会导致整个 调用 foo(bar) 减少到一个值,并且目标“评估 foo(bar)”被视为完成并被吹走。

当您刚接触编程时,人们通常不会告诉您延续。他们认为这是一个高级主题,只是因为 有一些非常高级的事情人们最终 有延续性。但事实是,每次调用函数时,您一直在使用它们。

【讨论】:

  • 这种想法在函数式语言中很常见,比如 LISP 和 Haskell,但我从未见过它在像 javascript 这样的过程语言中使用过。人们通常不会将它们视为延续,因为大多数过程语言将它们视为一种特殊情况。例如,您不能保存 alert(2 * _) 的延续以供以后在 Javascript 中使用(有一种不同的符号来做这种事情)。
  • @CortAmmon 虽然我有点同意你的看法,但请注意,Javascript 确实有相当强大的功能背景(它最初旨在作为具有类 C 语法的 Scheme 子集的实现),而它没有任何用于操作延续的标准函数(例如 call/cc)它在许多 javascript 库中使用延续传递样式的常见做法,因此在早期阶段理解这个概念对于JS 程序员。
  • 确实 Javascript 不做 first-class 延续。 return 内部的 foo 不是一个值;你不能把它打包到一个数据结构中,把它分配给一个变量,等一会儿然后再给它喂东西——你尤其不能喂它不止一次。但是,就像我说的,高级主题。
  • 同样,使用延续实现新的控制结构:高级。但是尝试实现一个行为类似于内置 if()then{}else{}my_if 函数,并且即使您允许自己使用其中的内置函数也未能这样做 - 不是非常先进,而且很有指导意义,特别是如果你最终会得到传递回调的代码。
【解决方案4】:

return 这里是红鲱鱼。也许有趣的是以下变化:

function add(a, b) {
  return (
    console.log(a + b),
    console.log(arguments)
  );
}

console.log(add(2, 2));

最后一行输出

undefined

因为函数实际上并没有返回任何东西。 (它会返回第二个console.log 的返回值,如果有的话)。

事实上,代码与

完全相同
function add(a, b) {
    console.log(a + b);
    console.log(arguments);
}

console.log(add(2, 2));

【讨论】:

    【解决方案5】:

    我有点震惊,这里没有人直接引用the spec

    12.9 return 语句语法 ReturnStatement : return ; return [这里没有 LineTerminator] 表达式;

    语义

    如果 ECMAScript 程序在语法上不正确,则 包含不在 FunctionBody 中的 return 语句。一种 return 语句导致函数停止执行并返回一个 对调用者的价值。如果省略表达式,则返回值为 不明确的。否则返回值为 Expression 的值。

    ReturnStatement 的评估方式如下:

    如果表达式不存在,则返回(return, undefined, empty)。 让exprRef 成为计算表达式的结果。 返回(return, GetValue(exprRef), empty)

    因此,由于规范,您的示例如下:

    return ( GetValue(exprRef) )

    在哪里 exprRef = console.log(a + b), console.log(arguments)

    根据spec on the comma operator...

    语义

    产生式 Expression : Expression , AssignmentExpression 是 评价如下:

    Let lref be the result of evaluating Expression.
    Call GetValue(lref).
    Let rref be the result of evaluating AssignmentExpression.
    Return GetValue(rref).
    

    ...意味着每个表达式都将被评估,直到逗号列表中的最后一项,它成为赋值表达式。所以你的代码return (console.log(a + b) , console.log(arguments)) 会去

    1.) 打印a + b的结果

    2.) 什么都没有执行,所以执行下一个表达式

    3.) 打印arguments,因为console.log() 没有指定 返回语句

    4.) 评估为未定义

    5.) 然后返回给调用者。

    所以正确的答案是,return 没有类型,它只返回某个表达式的结果。

    下一个问题:

    因此,我们可以将逗号分隔的表达式传递到 return 语句中。 这是一个函数吗?

    没有。 JavaScript 中的逗号是一个运算符,定义为允许您将多个表达式组合到一行中,并且由规范定义为返回列表中最后一个内容的计算表达式。

    你还不相信我吗?

    <script>
    alert(foo());
    function foo(){
        var foo = undefined + undefined;
        console.log(foo);
        return undefined, console.log(1), 4;
    }
    </script>
    

    使用该代码here 并弄乱列表中的最后一个值。它总是会返回列表中的最后一个值,在你的情况下它恰好是undefined.

    关于你的最后一个问题,

    从这个开始,我们可以大胆猜测一下,每个关键字 JavaScript 最终是一个函数?

    再次,不。 Functions have a very specific definition 的语言。我不会在这里转载,因为这个答案已经很长了。

    【讨论】:

      【解决方案6】:

      理解return 语句的有趣方法是通过void 运算符 看看这段代码

      var console = {
          log: function(s) {
            document.getElementById("console").innerHTML += s + "<br/>"
          }
      }
      
      function funReturnUndefined(x,y) {
         return ( void(x+y) );
      }
      
      function funReturnResult(x,y) {
         return ( (x+y) );
      }
      
      console.log( funReturnUndefined(2,3) );
      console.log( funReturnResult(2,3) );
      &lt;div id="console" /&gt;

      由于return 语句采用一个参数[[expression]] 并将其返回给堆栈上的调用者,即arguments.callee.caller,因此它将执行void(expression),然后返回undefined,这是对void 的评估运算符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-19
        • 1970-01-01
        • 1970-01-01
        • 2014-03-16
        • 2020-08-23
        • 2012-04-01
        • 1970-01-01
        • 2020-05-26
        相关资源
        最近更新 更多