19

On a lot of browsers I've tested, JavaScript blocks actually return a value. You can test it out in any console:

for(var i = 0; i < 10; i++) {
    var sqrt = Math.sqrt(i);
    if(Math.floor(sqrt) === sqrt) {
        i;
    }
}

The "return" value is the last square number, that is, 9! But since it isn't an expression I suppose, you can't do this:

for(var i = 0; i < 10; i++) {
    ...
} + 5

That doesn't work. It gives + 5, or 5, of course, because it's a separate statement. Putting the loop in parentheses obviously fails, and if a block is in parentheses (e.g. ({f(); r}) - doesn't work) it's treated as an object and throws a syntax error.

One way to take advantage of the return value, as such, is to use eval:

eval('for(var i = 0; i < 10; i++) {var sqrt = Math.sqrt(i);if(Math.floor(sqrt) === sqrt) {i;}}') + 5; // 14

But I'm obviously not going to want to use that if eval is the only solution. Is there a way to use a block's resultant value without using eval that I'm missing? I really like this feature :)

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • 4
    Blocks are statements, not expressions. Therefore, you can't use them as operands (you can't use operators on them)... – Šime Vidas Dec 23 '11 at 16:23
  • @ŠimeVidas: Exactly - is there a way to somehow get them to work? Not using `eval`? – Ry- Dec 23 '11 at 16:24
  • 1
    You should treat `eval`'s return value as the anomaly, not the restriction that JS "normally" (read: ignoring outside `eval`) imposes. Blocks are **not** expressions. –  Dec 23 '11 at 16:26
  • Ok, so `eval( '{ 1; }' ) + 1` evaluates to `2`, and you want this functionality because... – Šime Vidas Dec 23 '11 at 16:27
  • 1
    @ŠimeVidas: Because it's interesting is all. If there's a good way to get this to work in *any* JavaScript engine, it would be interesting to see all the syntactic sugar you can add :) Even if it's not for production purposes. – Ry- Dec 23 '11 at 16:29
  • Use a local function if you want to return a value from a block (make the block into a local function). – jfriend00 Dec 23 '11 at 16:31
  • @minitech From what I understand, the return values of statements are not supposed to (can't) be used by programs, but by the interpreter. For instance, the statement may throw which is then reflected by its return value... – Šime Vidas Dec 23 '11 at 16:32
  • @jfriend00: I *realize* that I can do that. I want to know if I can get away with just using the block. – Ry- Dec 23 '11 at 16:33
  • 1
    Are you sure that what you are observing is blocks returning values instead of consoles being designed to print the last thing that is evaluated? (which means that the console is really printing the final `i;` instead of what you think the for loop returns, assuming that in reality the for loop doesn't return anything). – slebetman Dec 23 '11 at 16:33
  • @slebetman: I'm sure. Using `eval`, it "works". – Ry- Dec 23 '11 at 16:34
  • I think that's an (mis)implementation-dependent thing, that might change in the future and that in any case is **not** "real" javascript. So I don't see the point of tinkering with that.. – redShadow Dec 23 '11 at 16:35
  • @redShadow: There's no point, it's for fun. – Ry- Dec 23 '11 at 16:36
  • OK, if you're just spending time on a curiousity, but I'd never use something like this in any real code. – jfriend00 Dec 23 '11 at 16:37
  • 1
    Are you sure that what you are observing is the block returning a value or just eval returning the last expression evaluated (in this case it is returning the last `i;` instead of what the for loop returns). – slebetman Dec 23 '11 at 16:38
  • 3
    @minitech `eval()` returns the completion value of the JavaScript program that has been passed in as source text. This is an interesting point. Other than this, I am not aware of any other method to receive the completion value of a program/statement... – Šime Vidas Dec 23 '11 at 16:39
  • @slebetman: Nope! That's why I'm asking :) – Ry- Dec 23 '11 at 16:40
  • @ŠimeVidas: Aha, thank you! Okay then, post that as an answer. – Ry- Dec 23 '11 at 16:40
  • See also [JavaScript BlockStatement confusion](http://stackoverflow.com/q/9803597/1048572) – Bergi Dec 07 '15 at 21:14

2 Answers2

17

In JavaScript, statements return values of the Completion type (which is not a language type, but a specification type).

The Completion type is used to explain the behaviour of statements (break, continue, return and throw) that perform nonlocal transfers of control. Values of the Completion type are triples of the form (type, value, target), where type is one of normal, break, continue, return, or throw, value is any ECMAScript language value or empty, and target is any ECMAScript identifier or empty.

Source: http://es5.github.com/x8.html#x8.9

So, eval() evaluates the program that has been passed in as source text. That program (like any JavaScript program) returns a Completion value. The second item in this Completion value (the "value" item) is returned by the eval() invocation.

So, with eval you are able to retrieve the completion value of an JavaScript program. I am not aware of any other method to accomplish this...

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • I'd bet that this completion type stuff is there just to specify some original `eval` implementation. – hugomg Dec 23 '11 at 16:50
  • 1
    @missingno I can assure you that the completion type is an essential part of the language. Completion values are used by the interpreter to determine the next step in execution (i.e. transfer control to the caller function, jump to the beginning of the iteration statement, etc.). – Šime Vidas Dec 23 '11 at 17:01
  • 1
    Sorry, for the confusion. I can't think of any case other then eval that cares about the completion *value*. (The second value in that triple, and the one that matters for this particular question) – hugomg Dec 23 '11 at 23:11
  • @hugomg throw and return needs those values, or else how are you expecting to "return" a value? It may be that eval is the only thing that cares about the value in a NORMAL typed completion. – DifferentPseudonym Mar 06 '20 at 07:00
8

There is a proposal for ES7 to introduce a do expression which allows any block to be turned into an expression. A do expression evaluates a block and returns its completion value.

Using this syntax, which you can try out today with Babel using the syntax-do-expression and transform-do-expression plugins, your example would look like this:

function lastSquareNumber(val) {
    return do { for(var i = 0; i < val; i++) {
        var sqrt = Math.sqrt(i);
        if(Math.floor(sqrt) === sqrt) {
            i;
        }
    }}
}

console.log(lastSquareNumber(10));
xmedeko
  • 7,336
  • 6
  • 55
  • 85
Robert Knight
  • 2,888
  • 27
  • 21
  • 1
    Cool! I somehow doubt that it’ll make it, but it’s good too see all the same. – Ry- Jan 17 '16 at 20:40
  • 1
    This looks very neat, I hope this gets implemented in ES7 tho we'll have to wait for that for another while I guess :) – SidOfc Jul 29 '16 at 10:23