Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with:

Using Logical Operators To Perform Ternary Operations In Javascript

By Ben Nadel on

Yesterday on Twitter, Cody Lindley pointed out that logical operators in Javascript (&& and ||) don't simply return True or False - they return specific operands used in the logical statement. While I have leveraged this concept in the past with statements like this:

  • // Logical OR in assignment.
  • var options = (options || defaultOptions);
  •  
  • // Logical AND in testing.
  • if (window.console && window.console.log){ .. }

... it was still hard for me to wrap my head fully around what the logical operators were really doing. If you look at the Mozilla Development Center documentation on logical operators, you will see that not only is the statement being evaulated for its truth, but one of the operands is returned based on that truth, even if the returned operand is NOT true. To help understand this, you have to stop thinking in terms of individual operand truths and concentrate on the statement-level truth (regardless of the individual values).

Thinking this way helped me clarify the situation in my mind (pseudo code):

UPDATE: This is not quite accurate; (a && b) retuns "a" only if "a" can be converted to false.

  • // Logical AND. ------
  •  
  • if (a && b){
  • return( b );
  • } else {
  • return( a );
  • }
  •  
  • // Logical OR. ------
  •  
  • if (a){
  • return( a );
  • } else {
  • return( b );
  • }

When you start to look at it this way, you think less about what values "a" and "b" contain and more just about how the AND / OR statements will be evaluated.

Even with this mentality, I still felt a bit fuzzy on how things were really working. As such, I needed an exercise that would force it into my brain. That's when I came up with the fun idea of duplicating the ternary operator in Javascript using logical operators. Here's what I came up with:

UPDATE: This does not quite work; will not return proper value if ifTrue is a falsey.

  • <script type="text/javascript">
  •  
  • // This method will use a simple tertiary operator to
  • // gather the description information based on the name.
  •  
  • var easyIIF = function( girl, ifTrue, ifFalse ){
  • return(
  • ((girl == "Tricia") ? ifTrue : ifFalse)
  • );
  • };
  •  
  •  
  • // This method will use a combination of logical operators to
  • // gather the description information based on the name.
  •  
  • var hardIIF = function( girl, ifTrue, ifFalse ){
  • return(
  • ((((girl == "Tricia") && ifTrue) || ifTrue) || ifFalse)
  • );
  • };
  •  
  •  
  • // ------------------------------------------------------ //
  • // ------------------------------------------------------ //
  •  
  •  
  • // Set the name of the girl.
  • var girl = "Tricia";
  •  
  • // Output using easy tertiary operator.
  • document.write(
  • easyIIF( girl, "Hot", "Nice" ) +
  • "<br />"
  • );
  •  
  • // Output using complex logical operators.
  • document.write(
  • hardIIF( girl, "Hot", "Nice" ) +
  • "<br />"
  • );
  •  
  • </script>

What I created above is two different IIF() style methods that perform a ternary operation of sorts; the first one, easyIIF() used the actual ternary operator while the second one, hardIIF(), uses a combination of logical operators to duplicate the same functionality. When we run the above code, we get the following output:

Hot
Hot

This is pretty fascinating stuff... and a bit terrifying; using logical operators to perform more than simple boolean evaluation is nowhere near readable and requires the programmer to have an exhaustive understanding of how logical operators work. I wrote this post as an exercise - not to recommend that people use this approach (except in the most commonly understood use-cases).




Reader Comments

That is interesting stuff. I sure hope, though, that I never have to work on code where someone decided to substitute that approach for ternary operators! It's just not at all readable to me, and would be easy for me to get outputs flipped in my mind as I'm working through the logic. You should burn this blog post immediately. lol

Reply to this Comment

@Doug,

Right?!? Even after writing it, I have to remind myself what it's doing. It's all about the short-circuit evaluation on the || :)

Reply to this Comment

hi, I just thought "huh?" but then when I viewed the script closely, I became gradually clear what you mean ^ ^ is not at easy to understand, I really must admit ^ ^

Reply to this Comment

I've already been doing the logical OR ( var options = (options || defaultOptions); ) and always felt like I was cheating or doing something I shouldn't be doing.

Reply to this Comment

Think about it this way:

When || is encountered, if the preceding expression was truthy, evaluation stops and that truthy value is returned.

When && is encountered, if the preceding expression was falsy, evaluation stops and that falsy value is returned.

Otherwise, things continue getting evaluated.

A truthy value is anything that == true, like 12 or "foo" or true, while a falsy value is anything that == false, like 0 or '' or undefined.

Where this is great is when you need to "test your way" into a property. For example:
var log = window.console && console.log;

1) window.console: truthy
2) &&: the preceding thing wasn't falsy, so continue.
3) console.log: it's the last statement, so its value is returned.

If you just tried this and console didn't exist, you'd get some errors:
var log = console.log;

Also, this..
var result = something ? something : otherthing;

..can be more succinctly written as:
var result = something || otherthing;

And because logical || and && short-circuit, that means when evaluation stops, nothing afterwards gets evaluated..

Meaning that in this example, if something is truthy, somefunction() will never get executed:
var result = something || somefunction();

Now, if you've made the "logical" leap here.. you can see that logical || and && can also be used.. to conditionally execute functions.

This..
something || somefunction();

..is equivalent to:
if ( !something ) { somefunction(); }

And this..
something && somefunction();

..is equivalent to
if ( something ) { somefunction(); }

So now you know what this code does:
window.console && console.log( "i'm only logged if window.console exists!" );

Either way, the end goal should be to write more readable, maintainable code though.. so if it looks complicated to you while writing it, it'll probably be about 5x worse to someone reading it six months from now, you know, someone like you :)

Reply to this Comment

@Sophie,

Yeah, funky stuff right?

@Todd,

I think that's one of those common use-cases that we all use. There are going to be nuances of complexity. Like, I think, as you are saying, that:

var a = (b || c);

... is pretty common. But, what about:

var a = (b && c);

... this gets a bit more fuzzy in my head (especially if you think that "a" will now hold a boolean).

But what about this?

var logger = ((window.console && window.console.log) || window.alert);

... definitely complex, but you sort of get the idea of what's going on.

I guess you just have to treat each situation as a judgment on usability and readability.

Reply to this Comment

@Ben, this is a perfect example of where *not* to use logical && and ||:

var logger = ((window.console && window.console.log) || window.alert);

What if console.log returns undefined? Then it logs AND alerts. I'm guessing that's not what you want.

This is probably what you want:
var logger = window.console ? console.log : alert;

Also, as an aside, note that you can't reference console.log in this way in all browsers. You should actually do:

var logger = window.console ? function(){ console.log.apply( console, arguments ); } : alert;

.. but that's why I've created a cross-browser console.log debugging wrapper:
http://benalman.com/projects/javascript-debug-console-log/

Reply to this Comment

@Cowboy,

I wasn't necessarily saying that you should do that - I was just pointing out that sometimes the complexity of a statement is made somewhat clear based on the items being referenced.

But, I am not sure what you are saying referring the undefined value? If console.log returns undefined, then the first statement would become false, and hence, window.alert would be returned.

I tested this in Internet Explorer, which doesn't have console.log and it went to the alert. Even if it's only partially defined:

window.console = {};
var logger = ((window.console && window.console.log) || window.alert);
alert( logger );

... IE still defers to window.alert.

I am not sure what you mean. Also, I am not sure how it can both "logs AND alerts" - can you explain that further? Wouldn't that require some sort of concatenation of functionality?

Reply to this Comment

@Cowboy,

Your cross-browser logger looks badass. I'll have to take a close look at that. Thanks for posting it!

Reply to this Comment

@Ben,

Sorry for the confusion.. the problem would be if you were actually invoking the function like:

var value = "foo";
window.console && console.log( value ) || alert( value );

In that case it would both log and alert, since even though console.log exists, when it's invoked it returns undefined, which is falsy.

Either way, a ternary removes all ambiguity:
window.console ? console.log( value ) : alert( value );

Still, you can't reference console.log like that win Webkit, it'll assplode all over the place.

Reply to this Comment

@Cowboy,

Ahhh, gotcha - sorry for the misunderstanding. I hate when Webkit assplodes... no one wants to start their day off that way :)

@Paul,

I'll take a look; sounds like maybe a good lead into one of your anti-pattern segments on YayQuery ;)

Reply to this Comment

Nice post Ben. I think it's okay to use basic stuff like:

function runFn(fn) {
return fn && typeof fn === 'function' && fn();
}

It may not seem readable to a JS beginner, but frankly, I write code assuming that another developer reading it will be proficient in the area. For me, a&&b&&b() is more readable than:

if(a){if(b){b()}}

Another interesting aspect of this topic is operator precedence and associativity, I discussed both in this post: http://james.padolsey.com/javascript/express-yourself/

E.g.

Does "a&&b||c" really mean "a&&(b||c)" or "(a&&b)||c" ... They are both very different. In this case, since &&'s precedence is higher than ||, it would be: "(a&&b)||c".

It's also interesting to note that expressions with multiple operators will sometimes be processed from right-to-left instead of the conventional left-to-right. For example, the ternary operator (a?b:c), if encountered more than once in a single expression, will evaluate from right-to-left:

a ? b : c ? d : e

is the same as:

a ? b : (c ? d : e)

but not the same as:

(a ? b : c) ? d : e

This is actually quite important -- it can cause a lot of confusion sometimes.

Reply to this Comment

Another example (that Paul and I have already discussed) of where function invocation can be an issue with logical || and && is in the slide he just mentioned:

data = window.JSON && JSON.parse(data) || eval('('+data+')');

If window.JSON exists, JSON.parse(data) is called.. but what if the initial data is "0"? That's valid JSON, and when parsed returns 0.. which is falsy. So at that point, eval('('+data+')') is actually called even though the JSON has already successfully been parsed.

Why not use:
data = window.JSON ? JSON.parse(data) : eval('('+data+')');

Well, if JSON.parse doesn't exist, you'll get an error. But you'd actually get that same error in the initial code.

So why not use:
data = (window.JSON && JSON.parse) ? JSON.parse(data) : eval('('+data+')');

It's the best of both worlds, logical && and a ternary!

Reply to this Comment

@James,

Case in point why I wake up happy every day that I have the option to litter my code with parenthesis - zero confusion on order of operations.

@Cowboy,

I think I vaguely remember Paul talking about that in his jQuery Performance talk. Maybe it was different (haven't looked at the slide yet), but I remember him talking about something where a result of zero would cause an issue.

I like your last example - to me, the combination of ternary and logical seems like the most clear.

Reply to this Comment

With the ternary-ish trick, you can force the first branch to always return true (and therefore always skip the second) regardless of what the first branch's expression evaluates to:

(exprTest && !(exprTestTrue && false)) || exprTestFalse;

ternary equivalent:
exprTest ? exprTestTrue : exprTestFalse;

Reply to this Comment

heh - you can take advantage of string truthiness and make it even shorter (and more unreadable) with this:

exprTest && (exprTestTrue || ' ') || exprTestFalse;

Reply to this Comment

@Jeremy,

Ha ha, you are a mad man. I had to read your first statement like 8 times before it made sense. Too much partial-result storage in my head.

Reply to this Comment

This exercise (and the resulting conversation) is one of the cool but frustrating things about programming to me.

It's cool to be able to write code to do things like you're discussing here: sometimes you can wrap up something that took 10-15 lines and compress it to 1-2 lines. The frustrating part is realizing that after you do that, no one else is going to be able to understand it, and if anything ever goes wrong with that part of the code, people are going to track you down to fix it ...

Reply to this Comment

OK - last one, but this is too hot not to share (worked in all my tests):

exprTest && exprTestTrue|1 || exprTestFalse;

bitwise ops FTW!

Reply to this Comment

David Gault just pointed out a typo in my example - easyIIF() was called twice; the second one should have been hardIIF(). The result is the same.

@Dave,

I hear what you're saying. I would err on the side of readability, when in doubt.

@Jeremy,

Bit-wise operations make my brain hurt - you are a mad scientist.

Reply to this Comment

Hold on.... I'm thinking this doesn't work now (specifically my example). If the ifTrue value is false. Working on it now.

Reply to this Comment

Regardless of the good conversation we've had here, I put some updates into the blog entry (in red) to signify some slight misunderstandings I had.

Reply to this Comment

hello ben. it's a funny script, but why do not create a rubric and give us a little more of these scripts, i think it would be fun to read ^ ^

Reply to this Comment

In excess it certainly reduces readability, but the idea that the operators return a value is pretty foundational to some languages.

For instance, ruby doesn't have a ternary operator, instead you use the logical operators.

An example from a recent project was:

# username is the first command line argument if it exists
# or instead we prompt for the username
username = ARGV[1] or Prompt.prompt("Username: ")

And another example where we emulate the ternary operator:

# format a URL for doing an http request where we need an int
URI.encode("reusetoken=#{reusetoken and 1 or 0}")

This certainly feels more readable than ?:

I was pretty disappointed to see the ternary operator added to CF9. I had hoped that the boolean operators would be uncrippled so that you'd no longer get the "X cannot be converted to a boolean" errors and instead get more JS like behavior. Perhaps in a later version.

Think:

#user.getEmail() or "Not Provided"#

vs

#len(user.getEmail()) ? user.getEmail() : "Not Provided"#

Reply to this Comment

@Elliott,

I personally like the ternary operator, but that's just a personal opinion.

Going back to the example you have regarding email, I made this just for you :) Hopefully it doesn't give you a heart attack:

<cffunction name="getFirstName">
<cfreturn "Ben" />
</cffunction>

<cffunction name="getLastName">
<cfreturn "" />
</cffunction>

<!--- Get the first name, or default to "Not Provided". --->
<cfset evaluate( "len( setVariable( 'firstName', getFirstName() ) ) || len( setVariable( 'firstName', 'Not Provided' ) )" ) />

<!--- Get the last name, or default to "Not Provided". --->
<cfset evaluate( "len( setVariable( 'lastName', getLastName() ) ) || len( setVariable( 'lastName', 'Not Provided' ) )" ) />

<!--- Output name values. --->
First Name: #firstName#<br />
Last Name: #lastName#

... When you run this, you get:

First Name: Ben
Last Name: Not Provided

... I feel so dirty now.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.