2013-06-01 5 views
7

Это выражение Javascript работает просто отлично во всех браузерах (jsfiddle):Выражения с условной и назначения оператора

false ? 1 : x = 2; 

Это вычисляемая 2.

Но почему? Я ожидал бы исключения здесь, потому что левая сторона задания - false ? 1 : x, что не является действительной ссылкой. Сравнение с (jsfiddle):

(false ? 1 : x) = 2; 

Это один бросает ReferenceError. Я дважды проверил Javascript operator precedence table, он утверждает, что условный оператор ? : имеет более высокий приоритет, чем оператор присваивания =, поэтому оба выражения должны быть идентичными, по крайней мере, хотя бы так.

В Java, который имеет довольно похожие синтаксис и правила приоритета оператора, такие как Javascript, оба выражения выше приводят к ошибке времени компиляции, что имеет смысл.

Может кто-нибудь объяснить эту разницу?

+3

Обратите внимание, что [условный оператор] (HTTP: //es5.github .io/# x11.12) возвращает значения, а не ссылки. – Teemu

+2

@Teemu - где _value_ может быть ссылкой. Например, '(false? Func1: func2)();' – nnnnnn

+0

@nnnnnn. Вы правы с этим, хотя OP имеет только переменную в примере. На самом деле вы упомянули об этом и в своем ответе, просто плохо читаете для меня ... – Teemu

ответ

9

Вот два ключа к пониманию разницы между условным выражением JavaScript и условным выражением Java:

Пожалуйста, прочитайте Примечания в нижней части этого раздела ECMAScript 5 аннотированных спецификаций:

http://es5.github.io/#x11.12

Теперь, пожалуйста, прочитайте раздел спецификации Java для условного выражения:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

Вы заметите, что, как ECMAScript 5 состояний Отметим, что третий операнд в тройном оператора в Java не может быть просто любой старое выражение - это может быть только УсловнымВаражением. Однако для ECMAScript 5 третьим операндом может быть любое AssignmentExpression.

Заглядывая в спецификации Java, мы видим, что выражение является любым выражением присваивания:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.27

Но УсловноеВыражением является либо УсловноеВыражением с тройным оператором (... ...: .. ,) или просто ConditionalOrExpression (называемое LogicalOrExpression в ES5) (см. одну из первых двух ссылок выше для этой информации). "Цепочка" из того, что ConditionalOrExpression может быть начинается здесь в Java:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.24

А вот в ECMAScript 5:

http://es5.github.io/#x11.11

После "цепочки" типов выражений назад в Спецификация ECMAScript 5 (потому что ее легче отслеживать, чем спецификацию Java) из ConditionalExpression полностью через все остальные выражения, но выражение Assignment Expression, наконец, приземляет нас в начале - первичное выражение:

http://es5.github.io/#x11.1

Второй операнд в обоих ваших фрагментов кода выше является первичным выражением:

1 

Кончилось все это rigamarole (если я правильно) является то, что в Java, третий операнд тернарного оператора не может быть присвоением, но в JavaScript он может. Вот почему оба ваших примера не работают в Java, но только в JavaScript.

Почему первый работает в JavaScript, но не во втором?

operand1 ? operand2 : operand3; 

работает как следующий IIFE код (не фактически, но ниже код иллюстрацией того, как вышеупомянутые работы):

(function() { if (operand0) return operand1; else return operand2;}()); 

Итак:

false ? 1 : x = 2; 

будет (снова, нет фактически - ниже код иллюстративный выше кода):

(function() { if (false) return 1; else return x = 2;}()); 

Однако в вашем втором фрагменте, при использовании скобок, вы выделите явно условное выражение из «= 2;» :

(false ? 1 : x) = 2; 

становится (опять же, не фактически - ниже код иллюстрируют выше код):

(function() { if (false) return 1; else return x;}()) = 2; 

В «действует как вызов функции Примера IIFE» поведение трехкомпонентного оператора будет возвращать все, что х и что будет значения, а не ссылка, которая не может быть назначена. Отсюда и ошибка. Это было бы как следующий код (если х === 3):

3 = 2; 

Очевидно, что один не может сделать это.

В Java, я считаю, первая дает ошибку, потому что третий оператор не может быть назначением, второй - ошибкой, потому что вы не можете назначить значение (как в JavaScript).

Насколько старшинство операторов, пожалуйста, посмотрите на следующий код:

var x = 3; 

console.log(false ? 1 : x);   // ?: evaluates to "3" 
console.log(false ? 1 : x = 2);  // ?: evaluates to "2" 
console.log(false ? 1 : x = 2, 4); // ?: evaluates to "2" - "2" and "4" arguments passed to log 
console.log((false ? 1 : x = 2, 4)); // ?: evaluates to "4" 

Первые два легко понять, если рассматривать с точки зрения IIFE иллюстративного кода выше.

В первой строке x оценивается и условное выражение оценивается в 3 - это легко.

Во второй строке наилучшим образом я могу описать это, что условный оператор (? :) заставляет даже оператор более низкого приоритета «=» оцениваться как полное выражение не потому, что (? :) имеет более высокий приоритет , а потому, что, как указывает спецификация, выражение присваивания, следующее за «:», оценивается (включая часть «= 2») в качестве присваивания. Это поведение выглядит более ясным в операторе return в примерах IIFE выше. По крайней мере, с JavaScript, вы можете иметь назначение не только во втором операнде, но и в третьем условном выражении.

Однако в третьей строке полное выражение присваивания уже найдено в выражении «x = 2», а тернарный оператор использует его как полный третий операнд, а оператор «,» имеет более низкий приоритет, чем любой другой, мы получаем эквивалент следующего кода:

console.log((false ? 1 : x = 2), 4); 

в четвертой строке коды, герметизирующий все выражение в заявлении console.log() в круглых скобках приносит «4' в»: 'тройное выражение как часть третьего операнда.

Следующие jsfiddles демонстрируют вышеуказанное обсуждение с живым кодом. Обратите внимание, что первые два имеют точно такую ​​же ошибку после печати '2' дважды:

FIDDLE1

FIDDLE2

FIDDLE3

+2

Можно утверждать, что эти функции, хотя и ведут себя как выражения, различны в семантике, но это не главное. Здесь очень много важных последствий. Поэтому мне потребовалось некоторое время, чтобы найти правила грамматики Java и Javascript, чтобы понять, как анализируются выражения. Я понимаю, что это не так просто, как могут предложить таблицы приоритетов операторов.Это имеет смысл, я не совсем прав, говоря, что синтаксис подобен. Это хороший ответ. –

+0

Отмеченные семантические различия. Хорошая точка зрения. Возможно, термины «помочь понять» или «аналогично» будут работать лучше? Я не мог придумать лучшего способа помочь продемонстрировать, что происходит в реальном коде, поэтому IIFE кажутся полезными в продвижении дискуссии, а также демонстрируют, по крайней мере, аналоги в jsFiddle. – Aeoril

+0

@ GOTO0 - Я обновил свой ответ, чтобы избавиться от слова «семантически», ссылаясь на мои аналоги IIFE, вместо этого используя термин «иллюстративный», – Aeoril

14

Как вы нашли в MDN, ? : имеет более высокую precendence, чем оператор присваивания =, что означает, что JS читает ваше заявление как:

false ? 1 : (x = 2); 

На первый взгляд это может показаться назад, но что это значит заключается в том, что ? : ожидает три операнда, а часть справа от : является третьим операндом. Так как = имеет более низкий приоритет x = 2 становится третьим операндом.

Оповещение показывает 2, поскольку назначение x = 2 устанавливает переменную x в 2, а затем это выражение (суб) вычисляется в 2.

Ваша вторая версия:

(false ? 1 : x) = 2; 

... дает эталонную ошибку, потому что это делает (false ? 1 : x) часть первая, которая определяет значение , связанного сx (undefined), он не возвращает переменную x сам. undefined = 2 не работает.

+1

Извините, я не понимаю первое предложение вашего ответа. Я, хотя операторы с более высоким приоритетом будут оцениваться первыми, а не последними. Почему здесь это другое? –

+1

Часть '?:' Обрабатывается сначала в этом смысле, что для того, чтобы она вообще была оценена, она должна иметь три операнда, поэтому интерпретатор должен выяснить, что такое операнды, и в этом случае третьим операндом является выражение 'x = 2'. Вот что я пытался сказать в моем втором абзаце выше. Обратите внимание, что если вы говорите «правда? 1: x = 2 '' 'x = 2' часть вообще не оценивается, а' x' сохраняет свое предыдущее значение. – nnnnnn

0

Я wouldn't код так, но тот факт, подмигнули работает для меня очевидно:

Начиная с вашим утверждением ?: имеет более высокий приоритет, чем = - thats's неправильно, поскольку ?: имеет 15 в то время как = имеет 17.
Поэтому первый код похож на false ? 1 : (x = 2); - оттуда его довольно ясно, Что происходит, на мой взгляд, тот похож на if (false) 1; else x=2;

Ваш второй со de snippet не может работать, потому что левая сторона = = undefined, и вы не можете назначить что-либо неопределенному, например, ссылки ReferenceError.(?)

+1

'?:' Имеет более высокий приоритет, чем '='. 15 - более высокий приоритет, чем 17, согласно объяснению в верхней части таблицы, связанной с OP. – nnnnnn

5

Тройной оператор необходим три значения: A условия, в если-истинном и , если ложно-.

В выражении

false ? 1 : x = 2 

компилятор видит ложное как условие, как если-истинным и х = 2. Поскольку ? имеет приоритет над = x = 2 еще не оценен. Так как ложь по своей природе неверна, то возвращается то, что возвращается. x = 2.

При оценке х = 2 возвращается 2.

Следовательно, почему вы можете написать

x = y = 2 

В этом примере х и у оба устанавливаются на 2.

Причина в целом x = 2 рассматривается как if-false операнд, а не только x - это потому, что тернарный оператор берет все справа от: как if-false, если он находится в пределах области действия.

Если вы используете скобки, однако то, что вы пытаетесь сделать, это установить литерал (любой x) на 2, что приведет к ошибке.

Я не могу ответить на вопрос о том, почему в javascript он работает, но в результате он вызывает ошибку в java. Я могу только предположить несколько разные приоритеты оператора?

Смежные вопросы