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-значение) из условного оператора:
- Пусть
lref
будет результатом вычисления логического выражения. - Если
ToBoolean(GetValue(lref))
истинно, тогда: - Пусть
trueRef
будет результатом вычисления первого выражения AssignmentExpression. - Возврат
GetValue(trueRef)
.
- Else
- Пусть
falseRef
будет результатом вычисления второго выражения AssignmentExpression. - Возврат
GetValue(falseRef)
.
GetValue
- внутренняя функция, которая преобразует ссылку на значение, поэтому почему вы получаете значение, а не ссылку, как вы ожидали.
1: Внутренний метод присваивания в ECMAScript не позволяет назначать не ссылки:
- Если
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. Без свойства ссылаться на объект становится анонимным.