Javascript Тернарный оператор lvalue

Я читал о тернарном операторе на разных языках и заметил что-то интересное в разделе Javascript. http://en.wikipedia.org/wiki/%3F:#JavaScript

Условный оператор в JavaScript имеет ту же структуру синтаксиса и приоритета, что и в других вариантах, основанных на BCPL, но существенная разница существует в семантике: она возвращает L-значение.

В первом предложении указано, что возвращение тернара в javascript является значением lvalue, поэтому я попробовал несколько примеров с нечетными результатами (в хром-консоли).

Дано:

var a = { 'yo' : 'momma' }
var b = { 'yo' : 'cool' }
var bool = true


(bool? a : b).yo = 'LLJ'
//a is now { 'yo' : 'LLJ' }

(bool? a.yo : b.yo) = 'LLJ' //throws a reference error

Почему первая работа и вторая сбой? (Логически они одни и те же утверждения, нет?)

Ответ 1

Нет (похоже, ссылка Wikipedia на "l-value" вводит в заблуждение) - она ​​возвращает значение аргумента, а не ссылку на него; значения в JavaScript не могут быть назначены непосредственно 1.

Если вы только что сделали следующее:

console.log(bool ? a.yo : b.yo);
// like doing the following:
'string' = 'eee';

... вы получите строку - вы не можете назначить строковое значение/литерал. Все ссылки на свойства преобразуются в их значение при передаче в условный оператор.

Однако, с объектом, ссылочным значением является объект, и поскольку свойство объекта является ссылкой, оно отлично работает.

console.log(bool ? a : b); // you get an object, it fine

Спецификация ECMAScript (стандартная версия JavaScript) говорит, что вы не можете получить ссылки (то есть l-значение) из условного оператора:

11.12 Условный оператор (?:)

  • Пусть lref будет результатом вычисления логического выражения.
  • Если ToBoolean(GetValue(lref)) истинно, тогда:
    • Пусть trueRef будет результатом вычисления первого выражения AssignmentExpression.
    • Возврат GetValue(trueRef).
  • Else
    • Пусть falseRef будет результатом вычисления второго выражения AssignmentExpression.
    • Возврат GetValue(falseRef).

GetValue - внутренняя функция, которая преобразует ссылку на значение, поэтому почему вы получаете значение, а не ссылку, как вы ожидали.

1: Внутренний метод присваивания в ECMAScript не позволяет назначать не ссылки:

8.7.2 PutValue (V, W)

  • Если Type(V) не ссылка, вывести исключение ReferenceError.
  • ... (остальное неважно, мой акцент)

Ответ 2

Поскольку вторая строка не ссылается на значение a.yo или b.yo, она ссылается на плоский объект.

Первое выражение заканчивается на .yo поэтому оно знает, что оно ссылается на значение a или b.

Ответ 3

Википедия ошибалась. Условный оператор возвращает r-значение, а не l-значение.

История статьи довольно интересная, поэтому я обобщил ее здесь:

  • 30 августа 2010 г.: Начало
    Создан раздел JavaScript. Правильно говорит, что в JavaScript тернарный оператор возвращает r-значение, но неправильно говорит, что в C/С++/Java он возвращает значение l. Только в С++ тернарный оператор возвращает l-значение.

  • 31 января 2011 г.: Не может дать l-значение в C
    C правильно удаляется из раздела JavaScript, потому что он не возвращает l-значение. Java остается.

  • 15 февраля 2011 г.: "Исправлено"
    Сравнение с Java и С++ удалено (комментарий правильно говорит о том, что Java никогда не давал l-значение), но о нет! JavaScript внезапно возвращает значение l!

  • 7 марта 2011 г.: Надежда восстановлена ​​...
    Неверное значение "l-value" изменяется на "значение", ссылаясь на "Значение" статьи (в котором описываются как l-значения, так и значения r).

  • 7 марта 2011 г.: ... но не надолго
    Текст ссылки изменяется на "l-value".

  • 7 сентября 2013 г.: Три ура для Qantas 94 Heavy!
    Благодаря этому вопросу, Википедия была исправлена.

Ответ 4

Имеет отношение к тому, как js фактически реализован, я думаю... Но подумайте об этом. (bool? a: b) дают так, что код становится a.yo = 'LLJ', что действительно. (bool? a.yo: b.yo) дает то, что когда-либо держит a.yo. По существу, вы делаете 'moma' = 'LLJ', который недействителен.

Ответ 5

Здесь d становится вашей переменной набора.

    var obj = {'d' : 1, 'd1': 2}, obj2 = {'d': 2, 'd1': 2}, bool = true;
    var dummyFn = function(obj, k, v) { obj['k'] = val; return obj; };
    (bool ? (dummyFn(obj, 'd', (obj.d = newVal + 1))) : obj).d1  = newVal = 4;
    console.log(obj.d);

Причина, по которой код не работал, будет той же причиной, по которой вы не сможете заменить значение dummyFn с obj. Без свойства ссылаться на объект становится анонимным.