Question

I was reading about the ternary operator in different languages, and noticed something interesting in the Javascript section. http://en.wikipedia.org/wiki/%3F:#JavaScript

The conditional operator in JavaScript has the same syntax and precedence structure as in the other BCPL-derived variants, but a significant difference exists in the semantics: it returns an l-value.

The first sentence states that the return of the ternary in javascript is an lvalue, so I tried some examples, with odd results (in the chrome console).

Given:

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

Why does the first work and the second fail? (Logically they're the same statements, no?)

Was it helpful?

Solution

Nope (it seems that Wikipedia's reference to "l-value" is misleading) - it is returning the value of the argument, not the reference to it; values in JavaScript cannot be assigned to directly1.

If you just did the following:

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

... you would get a string - you can't assign to a string value/literal. All property references are converted to their value when passed into the conditional operator.

However, with an object, the reference value is an object, and since the property of an object is a reference, it works fine.

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

The ECMAScript specification (that's the standard version of JavaScript) says that you can't get references (i.e. a l-value) from the conditional operator:

11.12 Conditional Operator ( ? : )

  1. Let lref be the result of evaluating LogicalORExpression.
  2. If ToBoolean(GetValue(lref)) is true, then:
    • Let trueRef be the result of evaluating the first AssignmentExpression.
    • Return GetValue(trueRef).
  3. Else
    • Let falseRef be the result of evaluating the second AssignmentExpression.
    • Return GetValue(falseRef).

GetValue is an internal function that converts a reference to a value, therefore that's why you get a value, not a reference as you expected.

1: The internal assignment method in ECMAScript does not allow non-references to be assigned to:

8.7.2 PutValue (V, W)

  1. If Type(V) is not Reference, throw a ReferenceError exception.
  2. ... (the rest is unimportant, my emphasis)

OTHER TIPS

Wikipedia was wrong. The conditional operator returns an r-value, not an l-value.

The history of the article is quite interesting, so I've summarised it here:

  • 30 August 2010: The Beginning
    JavaScript section created. Correctly says that in JavaScript the ternary operator returns an r-value, but incorrectly says that in C/C++/Java it returns an l-value. Only in C++ the ternary operator returns an l-value.

  • 31 January 2011: Cannot yield an l-value in C
    C correctly removed from the JavaScript section because it doesn't return an l-value. Java remains.

  • 15 February 2011: "Corrected"
    The comparison to Java and C++ is removed (the comment correctly says that Java never yielded an l-value), but oh no! JavaScript suddenly returns an l-value!

  • 7 March 2011: Hope is restored...
    The incorrect "l-value" is changed to "value", linking to the Value article (which describes both l-values and r-values).

  • 7 March 2011: ...but not for long
    The link text is changed to say "l-value".

  • 7 September 2013: Three cheers for Qantas 94 Heavy!
    Thanks to this question, Wikipedia has been corrected.

Because the second line is not referencing the value of a.yo or b.yo, its referencing a flat object.

The first expression ends with .yo so it knows to reference the value of either a or b.

Has to do with how the js is actually implemented i guess... But consider this.. (bool ? a: b) give a so the code becomes a.yo = 'LLJ', which is valid.. (bool ? a.yo : b.yo) gives what ever the string a.yo is holding. Essentially you're doing 'moma' = 'LLJ' which is invalid.

Here d becomes your set variable.

    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);

The reason the code didn't work would be the same reason you can't replace dummyFn's value with obj. Without a property to reference the object becomes anonymous.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top