Может кто-нибудь объяснить, как работают арифметические выражения javascript?

Давайте оценим этот код для замены двух переменных:

var a = 5, b = 3; 
a = ( a -( b = ( a = a+b ) - b ) );

Я ожидал, что новое значение a должно быть 3, а значение b должно быть 5. Но я получил значения как a = 0 and b = 5;

Я пошел и прочитал арифметическое выражение EcmaScript и обнаружил, что оно будет вычисляться слева направо. (но не совсем понятно).

Итак, я попробовал это

var a = 5, b = 3; 
a = ( ( b = ( a = a+b ) - b ) - a );

Теперь у меня a = -3 and b = 5. Может кто-нибудь объяснить мне, почему так происходит?


person Shidhin Cr    schedule 27.09.2013    source источник


Ответы (5)


Как вы сказали, выражение оценивается слева направо, то есть при первом обнаружении a его значение по-прежнему равно 5. Это сводится к:

var a = 5, b = 3; 
a = ( a -( b = ( a = a+b ) - b ) );
a = 5 - (b = (a=(5+3)) - b);
a = 5 - (b = 8 - b);
a = 5 - 5; AND b = 5

Во втором, значение a оценивается после присваивания, потому что оно находится справа

var a = 5, b = 3;
a = ( ( b = ( a = a+b ) - b ) - a );
a = ( ( b = 8 - b ) - a ); AND a = 8
a = ( 5 - 8 ); AND a = 8; AND b = 5;
a = - 3;

Все сводится к порядку вычисления операндов.

Обычно в первом случае a оценивается как 5, затем оценивается b = ( a = a+b ) - b, и только во время этой оценки значение a изменяется, но не переносится обратно.

Во втором примере сначала вычисляется ( b = ( a = a+b ) - b ), меняя значение a на 8, затем вычисляется a, и оказывается, что оно равно 8.

Более тривиальный пример:

var a = 5
a = a + (a = 2)
// a = 7

a оценивается как 5, затем (a = 2) оценивается как 2 и a устанавливается в 2, затем оценивается 5+2 и устанавливается значение 7.

С другой стороны :

var a = 5
a = (a = 2) + a
// a = 4

(a = 2) оценивается как 2 и a устанавливается на 2, затем a оценивается как 2, затем оценивается 2+2 и a устанавливается на 4

person njzk2    schedule 27.09.2013

Вот объяснение, я надеюсь, оно ясное, потому что оно не так очевидно. JS и все другие языки создают дерево для оценки выражений. В дереве каждому оператору присваивается вес в зависимости от их положения и фигурных скобок.

Во-первых, вот шаги, которые JS обработал бы выражением:

Step 0. a = ( a - ( b = ( a = a+b ) - b ) );   a=5, b=3
Step 1.                   a = a+b              a=8, b=3
Step 2.             b = a - b                  a=8, b=5 
Step 3. a = a - b                              a=0, b=5

На каждом шаге обрабатывается один оператор.

И это дерево, которое он создает из вашего выражения:

    =
  /   \
 a     - 
     /    \
   a       =
          /  \   
         b    -            
             / \
            =   b
           /  \
          a    +
              / \
             a   b

Затем дерево обрабатывается снизу вверх.

person KLiFF    schedule 27.09.2013
comment
Но на третьем шаге a = a-b должен дать мне результат a=3 and b=5. не так ли? Поскольку шаг 2 изменил значение a and b на a = 8 and b =3. Я немного запутался - person Shidhin Cr; 27.09.2013
comment
Посмотрите на мой ответ: stackoverflow.com/a/19045816/1324929. Что @KLiFF не упоминает, так это то, что с присваиванием первым шагом является оценка выражения с правой стороны. Таким образом, a = (a - (b = (a = a + b) - b)) становится a = (5 - (b = (a = 5 + 3) - 3)). После этого правая сторона, от внутренних парен к внешним, a = 5 + 3, a становится 8, b = 8-3, b становится 5 и, наконец, a = 5-5, a становится 0, b остается 5. - person itmitica; 27.09.2013
comment
не совсем. Дерево обрабатывается слева направо, в противном случае a = 8 в вашем первом левом узле. - person njzk2; 27.09.2013
comment
Это действительно очень сбивает с толку, но вот объяснение: a слева уже был оценен и имеет значение 5, a справа является результатом вычислений из правой ветви дерева и имеет значение of 8. Итак, на самом деле существует a и a '. - person KLiFF; 27.09.2013
comment
Как именно обрабатывается дерево? Это снизу вверх ИЛИ слева направо? Я читал, что арифметические выражения оцениваются слева направо. Если я применяю здесь ту же логику, тогда это имеет смысл .. Он дает правильный вывод .. Мне нужно еще одно уточнение: при обработке левого узла он должен обрабатывать все его дочерние узлы перед переходом к правому узлу. .. не так ли? - person Shidhin Cr; 27.09.2013
comment
Это верно, в этом примере буква «b» справа от ( a = a+b ) - b оценивается после буквы «b» с левой стороны. вот код, который вы можете использовать, чтобы увидеть, какая ветвь в каком порядке оценивается с помощью firebug: var a = 5, b = 3; var say = function(t, v) { console.log(t + "." + v); return v;} a = say("left", a) -( b = ( a = a + say("left-b", b) ) - say("right-b", b)) - person KLiFF; 27.09.2013

var a = 5, b = 3;
a = ( ( b = ( a = a+b ) - b ) - a );

Так он и будет работать.

a = a + b //8
b = a - b //8-3=5
a = b - a //5-8=-3
person Cameron    schedule 27.09.2013
comment
Почему a = (a - (b = (a = a + b) - b)); дал мне a=0 and b=5 - person Shidhin Cr; 27.09.2013
comment
Да, я предполагаю, что a установлено в 5 перед выражением (a = a + b), поэтому a остается 5. - person Cameron; 27.09.2013

var a = 5, b = 3; 
a = ( a -( b = ( a = a+b ) - b ) );

1) a = a (5) - (остальная часть выражения, где скоро будут вычислены 2 части)

2) b = (a = a + b // и он все еще не вычислен) - b (который равен 3, потому что новый b еще не изменен)

3) a = 5(a) + 3(b) = 8

4) b (мы вернулись к пункту 2) = 8 (новый a) - 3 (старый b) = 5

5) а (точка 1) = 5 (старый а) - 5 (новый б) = 0

Новый a = 0, новый b = 5

var a = 5, b = 3; 
a = ( ( b = ( a = a+b ) - b ) - a );

1) b = ( a = a+b ) - 3(b)

2) a = 5(a) + 3(b) = 8

3) б = 8 (новый а) - 3 (старый б) = 5

4) а (финал) = 5 (новый б) - 8 (новый а) = -3

Новый a = -3, новый b = 5

Надеюсь читабельно: D

person Nikolay Talanov    schedule 27.09.2013

a = ( a -( b = ( a = a+b ) - b ) ) 

похоже на это

a = ( 5 -( b = ( a = 5+3 ) - 3 ) ) // a = 8
// a = ( 5 - ( b = 8 - 3 ) ) // b = 5
// a = ( 5 - 5 ) // a = 0

это означает, что он заменит a и b в левой части = за один раз с одинаковыми начальными значениями, это не похоже на эту последовательность

a = a+b; // a = 8
b = a-b; // b = 5
a = a-b; // a = 3

как вы ожидаете.

person itmitica    schedule 27.09.2013