Использование возвращаемого значения блока в JavaScript

Во многих браузерах, которые я тестировал, блоки JavaScript фактически возвращают значение. Вы можете проверить это в любой консоли:

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

«Возвращаемое» значение — это последнее квадратное число, то есть 9! Но поскольку это не выражение, я полагаю, вы не можете сделать это:

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

Это не работает. Конечно, это дает + 5 или 5, потому что это отдельный оператор. Помещение цикла в круглые скобки явно не работает, и если блок находится в круглых скобках (например, ({f(); r}) - не работает), он обрабатывается как объект и выдает синтаксическую ошибку.

Один из способов воспользоваться возвращаемым значением как таковым — использовать eval:

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

Но я, очевидно, не захочу использовать это, если eval — единственное решение. Есть ли способ использовать результирующее значение блока без использования eval, который мне не хватает? Мне очень нравится эта функция :)


person Ry-♦    schedule 23.12.2011    source источник
comment
Блоки — это операторы, а не выражения. Следовательно, вы не можете использовать их в качестве операндов (вы не можете использовать над ними операторы)...   -  person Šime Vidas    schedule 23.12.2011
comment
@ŠimeVidas: Точно, есть ли способ заставить их работать? Не используете eval?   -  person Ry-♦    schedule 23.12.2011
comment
Вы должны рассматривать возвращаемое значение eval как аномалию, а не ограничение, которое обычно накладывает JS (читай: игнорирование вне eval). Блоки не являются выражениями.   -  person    schedule 23.12.2011
comment
Итак, eval( '{ 1; }' ) + 1 оценивается как 2, и вам нужна эта функциональность, потому что...   -  person Šime Vidas    schedule 23.12.2011
comment
@ŠimeVidas: Потому что все интересно. Если есть хороший способ заставить это работать в любом движке JavaScript, было бы интересно увидеть весь синтаксический сахар, который вы можете добавить :) Даже если это не для производственных целей.   -  person Ry-♦    schedule 23.12.2011
comment
Используйте локальную функцию, если вы хотите вернуть значение из блока (превратите блок в локальную функцию).   -  person jfriend00    schedule 23.12.2011
comment
@minitech Насколько я понимаю, возвращаемые значения операторов не должны (не могут) использоваться программами, а интерпретатором. Например, оператор может бросить, что затем отражается его возвращаемым значением...   -  person Šime Vidas    schedule 23.12.2011
comment
@jfriend00: Я понимаю, что могу это сделать. Я хочу знать, смогу ли я просто использовать блок.   -  person Ry-♦    schedule 23.12.2011
comment
Вы уверены, что то, что вы наблюдаете, это блоки, возвращающие значения, а не консоли, предназначенные для печати последней оцениваемой вещи? (что означает, что консоль действительно печатает окончательный i; вместо того, что, как вы думаете, возвращает цикл for, предполагая, что на самом деле цикл for ничего не возвращает).   -  person slebetman    schedule 23.12.2011
comment
@slebetman: я уверен. Используя eval, это работает.   -  person Ry-♦    schedule 23.12.2011
comment
Я думаю, что это зависит от (неправильной) реализации, что может измениться в будущем, и в любом случае это не настоящий javascript. Так что не вижу смысла заморачиваться с этим..   -  person redShadow    schedule 23.12.2011
comment
@redShadow: Нет смысла, это для удовольствия.   -  person Ry-♦    schedule 23.12.2011
comment
Хорошо, если вы просто тратите время на любопытство, но я бы никогда не использовал что-то подобное в каком-либо реальном коде.   -  person jfriend00    schedule 23.12.2011
comment
Вы уверены, что то, что вы наблюдаете, является блоком, возвращающим значение, или просто eval, возвращающим последнее вычисленное выражение (в этом случае он возвращает последнее i; вместо того, что возвращает цикл for).   -  person slebetman    schedule 23.12.2011
comment
@minitech eval() возвращает значение завершения программы JavaScript, которая была передана в качестве исходного текста. Это интересный момент. Кроме этого, я не знаю другого способа получить значение завершения программы/инструкции...   -  person Šime Vidas    schedule 23.12.2011
comment
@slebetman: Нет! Потому и спрашиваю :)   -  person Ry-♦    schedule 23.12.2011
comment
@ŠimeVidas: Ага, спасибо! Хорошо, тогда опубликуйте это как ответ.   -  person Ry-♦    schedule 23.12.2011
comment
См. также путаницу с оператором блока JavaScript.   -  person Bergi    schedule 08.12.2015


Ответы (2)


В JavaScript операторы возвращают значения типа Completion (это не тип языка, а тип спецификации).

Тип завершения используется для объяснения поведения инструкций (break, continue, return и throw), которые выполняют нелокальную передачу управления. Значения типа завершения представляют собой тройки вида (тип, значение, цель), где тип – единица. из нормального, разрыва, продолжения, возврата или броска, value — любое значение языка ECMAScript или пусто, а target – любой идентификатор ECMAScript или пусто.

Источник: http://es5.github.com/x8.html#x8.9

Итак, eval() оценивает программу, которая была передана как исходный текст. Эта программа (как и любая программа JavaScript) возвращает значение завершения. Второй элемент в этом значении завершения (элемент «значение») возвращается вызовом eval().

Итак, с помощью eval вы можете получить значение завершения программы JavaScript. Я не знаю другого способа сделать это...

person Šime Vidas    schedule 23.12.2011
comment
Могу поспорить, что этот материал типа завершения существует только для того, чтобы указать некоторую оригинальную реализацию eval. - person hugomg; 23.12.2011
comment
@missingno Могу заверить вас, что тип завершения является неотъемлемой частью языка. Значения завершения используются интерпретатором для определения следующего шага выполнения (т. е. передачи управления вызывающей функции, перехода к началу оператора итерации и т. д.). - person Šime Vidas; 23.12.2011
comment
Извините, за путаницу. Я не могу придумать ни одного другого случая, кроме eval, который заботится о завершении value. (Второе значение в этой тройке и то, что имеет значение для этого конкретного вопроса) - person hugomg; 24.12.2011
comment
@hugomg throw и return нужны эти значения, иначе как вы ожидаете вернуть значение? Возможно, eval — это единственное, что заботится о значении в НОРМАЛЬНОМ типизированном завершении. - person MindlessRanger; 06.03.2020

В ES7 предлагается ввести выражение do, которое разрешает любой блок превратиться в выражение. Выражение do оценивает блок и возвращает его значение завершения.

Используя этот синтаксис, который вы можете попробовать сегодня с Babel, используя syntax-do-expression и transform-do-expression, ваш пример будет выглядеть следующим образом :

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));
person Robert Knight    schedule 17.01.2016
comment
Прохладный! Я как-то сомневаюсь, что получится, но тоже хорошо видно все таки. - person Ry-♦; 17.01.2016
comment
Это выглядит очень аккуратно, я надеюсь, что это будет реализовано в ES7, хотя нам придется подождать этого еще некоторое время, я думаю :) - person SidOfc; 29.07.2016