Expressions in JavaScript Ternary Operator and JSLint - javascript

I recently received a comment on one of my blog posts about JSLint asking why JSLint threw an error with the following:
s === "test" ? MyFunc() : MyFunc2();
The error generated was:
"Expected an assignment or function
call and instead saw an expression."
Clearly JSLint is expecting an assignment here, somthing more like:
var y = (s === "test") ? MyFunc() : MyFunc2();
But, I don't really see the problem with the first example. Is it really the case that ternary operators should only be used for assignments?
I couldn't see anything on JSLint.com, nor was there anything apparent in the book JavaScript: The Good Parts. And, the same error is also reported in the community fork JSHint.
Anyone?

It's an expression. It's equivalent to writing
0 === 1;
You're writing an expression that has immediate side effects and that's considered bad.
Generally expressions are useless statements that have no side effect. It's considered better form to simply do
if (s === "test") {
MyFunc();
} else {
MyFunc2();
}
Apart from that it's perfectly solid syntax. I personally do agree that writing a terse ternary as an alternative to an if is bad and you're better off only using it for assignment.
Other short hand expression that have been (ab)used for terse-ness
someCondition && doMagic(magic);
someCondition || doMagic(magic);
Again these are considered bad form if there used only as expressions because using these just obscures logic away and make it harder to maintain code.
JSHint has an option expr for this. See ticket
Running:
/*jshint
expr: true
*/
var s, MyFunc, MyFunc2;
s === "test" ? MyFunc() : MyFunc2();
0 === 1;
Will pass

Related

Understand ES6 Shorthand [duplicate]

Can I write the if else shorthand without the else?
var x=1;
x==2 ? dosomething() : doNothingButContinueCode();
I've noticed putting null for the else works (but I have no idea why or if that's a good idea).
Edit: Some of you seem bemused why I'd bother trying this. Rest assured it's purely out of curiosity. I like messing around with JavaScript.
What you have is a fairly unusual use of the ternary operator. Usually it is used as an expression, not a statement, inside of some other operation, e.g.:
var y = (x == 2 ? "yes" : "no");
So, for readability (because what you are doing is unusual), and because it avoids the "else" that you don't want, I would suggest:
if (x==2) doSomething();
This is also an option:
x==2 && dosomething();
dosomething() will only be called if x==2 is evaluated to true. This is called Short-circuiting.
It is not commonly used in cases like this and you really shouldn't write code like this. I encourage this simpler approach:
if(x==2) dosomething();
You should write readable code at all times; if you are worried about file size, just create a minified version of it with help of one of the many JS compressors. (e.g Google's Closure Compiler)
Another option:
x === 2 ? doSomething() : void 0;
If you're not doing the else, why not do:
if (x==2) doSomething();
Using null is fine for one of the branches of a ternary expression. And a ternary expression is fine as a statement in Javascript.
As a matter of style, though, if you have in mind invoking a procedure, it's clearer to write this using if..else:
if (x==2) doSomething;
else doSomethingElse
or, in your case,
if (x==2) doSomething;
A small addition to this old thread..
If you're evaluating an expression inside a for/while loop with a ternary operator and want to continue or break as a result - you're going to have a problem because both continue & break aren't expressions; they're statements without any value.
This will produce Uncaught SyntaxError: Unexpected token continue
for (const item of myArray) {
item.value ? break : continue;
}
If you really want a one-liner that returns a statement, you can use this instead:
for (const item of myArray) {
if (item.value) break; else continue;
}
P.S - This code might raise some eyebrows. Just saying.. :)
Technically, putting null or 0, or just some random value there works (since you are not using the return value). However, why are you using this construct instead of the if construct? It is less obvious what you are trying to do when you write code this way, as you may confuse people with the no-op (null in your case).
Probably shortest (based on OR operator and its precedence)
x-2||dosomething()
let x=1, y=2;
let dosomething = console.log;
x-2||dosomething('x do something');
y-2||dosomething('y do something');

What if I am ok with 'Cannot read property '****' of undefined'?

I have some code which looks like following:
$scope.query.toLowerCase() != 1
If query is not defined - there is an error in the console. Actually the code still produces the result that I want, but what is the right approach of dealing with error? Of course I can check the the variable in advance - but that will lead to more code, not sure of the benefits of hiding errors by having to read more code.
Of course I can check the the variable in advance - but that will lead to more code
Writing more code doesn't make your code bad, unless you are in a code golf competition. If you don't handle your errors and edge cases, then your program will not be reliable. If a line of code throws an error, your code might even terminate prematurely.
An alternative way to deal with possible undefined properties is to define a default value instead of checking if it is undefined.
($scope.query || "").toLowerCase() != 1 // this is enough to fix your expression
or more formally
($scope.query === undefined ? "" : $scope.query).toLowerCase() != 1
This obviously depends on what your purpose is.
Another example:
function add(a, b){
return a + (b || 0); // if b is falsy, assume b is 0
}
add(1, 2); // 3
add(1); // 1
Shortcircuiting is very useful in some cases, but make sure you know exactly how it works because misusing it will create unexpected behaviors.
Its bad practice to do ...
try {
$scope.query.toLowerCase() != 1
} catch(e) {
// do nothing
}
since this obscures errors and if an unexpected error happens you wont know about it which makes debugging a nightmare.
The approved practice is to write a few extra lines of defensive code to protect against the scenario that would lead to an error.
Yes its more verbose but its better than having a codebase which is a nightmare to debug.
in general: I'm outsorcing this task into a tiny helper:
function string(v){ return v==null? "": String(v) }
and I can be sure, that the output is always a String (Type-safety).
string($scope.query).toLowerCase() != 1
In this special case? you are checking against a number, you don't need the toLowerCase()
+$scope.query !== 1
would be even better in this case; an no need to deal with null or undefined.

Why does JSHint dislike ternaries for method calls on objects?

JSHint give the following error:
Expected an assignment or function call and instead saw an expression.
For the following line of code:
(aFunctionOrNull) ? aFunctionOrNull() : someObject.someMethod();
It highlights the final ) on someMethod so I assume the error is there. The code works and JSHint doesn't have a problem when I change it to if () {} else {} syntax. I don't mind the longer syntax but I'd like to learn why JSHint says this and if this is a bad practice.
The biggest piece of confusion may come from the terminology. Is someObject.someMethod() not a function call?
Well, in general it's considered bad practice to call a function using the ternary operator(s), without assigning the return value (which is what you seem to be doing).Also, it could be worth checking what JSHint has to say about the following code:
(aFunctionOrNull || someObject.someMethod)();
If aFunctionOrNull is undefined (or null, or falsy), the logical-or-bit will cause the expression to evaluate to someObject.someMethod, and the resulting value of that is invoked (a reference to a function object, hopefully). This gives you the opportunity to write your code more "fail-safe" without the bulk of a nested ternary:
(aFunctionOrNull || someObject.someMethod || function(){})();
The grouped expression is now bound to evaluate to a truthy value, so no errors are thrown there.
To avoid JSHint nagging about your not doing anything with the return value, either assign it to a variable (which I don't really like doing), or add a little operator to the mix:
~(aFunctionOrNull || someObject.someMethod || function(){})();//bitwise not
!(aFunctionOrNull || someObject.someMethod || function(){})();//logical not, doesn't really matter which one
On your last question: someObject.someMethod is indeed a function call. More specifically, it's a call to a function object in the someObject's context.
For those who don't know this: JS functions are objects, and the called context is either explicitly set using the bind method (defined on the Function.prototype) or ad-hoc:
var referenceToMethod = someObject.someMethod;
referenceToMethod();//<-- inside the function objects, this now points to the global object
An easy way to think of it is that JS functions just float around aimlessly in memory/space/time, until they are called via a reference, the context of that reference is then passed to the function object, to determine what object it'll interact with. This is, sadly, the global object by default, or null in strict mode.
JSHint says about expressions, or expr:
This option suppresses warnings about the use of expressions where
normally you would expect to see assignments or function calls. Most
of the time, such code is a typo. However, it is not forbidden by the
spec and that's why this warning is optional.
While JSLint says:
An expression statement is expected to be an assignment or a
function/method call or delete. All other expression statements are
considered to be errors.
AFAIK, there's no problem in doing what you're doing only that it will issue a warning because it would expect you to use an if..else statement, but you can turn this off in JSHint with:
/*jshint expr:true */
There error is because a ternary is an expression. You could use it to set a variable:
var result = a ? b : c;
Notice that the ternary evaluates to either b or c. It's an expression.
That said, the warning (I believe) comes from the notion that ternaries suffer poorer readability than an if...else block. The code above can be rewritten
var result;
if (a) {
result = b;
} else {
result = c;
}
Which is easier to read than a ternary. JSHint does as much to promote readable code as it does valid code. If you're comfortable including these expressions in your code, go ahead and disable the warnings for expressions. (It's what I would do.)

Is this line from Underscore.js doing equality checking actually necessary?

I've just been looking at the _.isEqual function of Underscore.js and a section of the code goes something like this:
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (a == b) return true;
I'm just wondering if there's any case where the third statement could be reached and evaluate to true?
Edit: Just to be clear, this isn't my own code I'm talking about, I'm reading the source of Underscore, in particular, this line and I was curious about why they're doing that.
I've just been browsing through the Underscore repo and came across a short discussion where someone asked the exact same thing, and it looks like it is actually unnecessary.
Following the algorithm defined by the ECMAScript Language Specification in section 11.9.6 and section 11.9.3 seems to show that no pair of values should return true in the above case.
So, in short, no, that situation is not possible.
The only situation where == and === react unexpectedly is when comparing a literal string ("123") to a constructed string (new String("123")), which would fail the first test.
However, on the second test it gets caught because the constructed string has the type object, but the literal has the type string.
Based on that, I'd say no, the third statement can never be reached, and evaluate to true.
When you use the == operator and the expressions are of different types, JavaScript will generally convert the two into the same type before comparing.
For example, this can happen with null and undefined. null == undefined is true, even though null === undefined is false. However typeof null is "object", while typeof undefined is "undefined". So, in this case you should return false on the second statement.
You can read all the details in the spec (section 11.9.3), it is very involved:
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
My initial guess was that it was to work around a broken browser implementation.
However after digging into the git logs for that file it looks like the corresponding line was in the very first underscore.js checkin. (I'm not gonna hunt in the parent documentcloud core.js repo...) You can see it at line 334 of https://github.com/documentcloud/underscore/commit/02ede85b539a89a44a71ce098f09a9553a3a6890 .
So now my guess is that its just cruft that got left in, never completely tested and never cleaned out.

Conditional Operators in Javascript

Is it ok to use conditional operators like a statement like so?
(x == y) ? alert("yo!") : alert("meh!");
Or is it more correct to use it to assign a value like so?
z = (x == y) ? "yo!" : "meh!";
If it's not incorrect to use it like a statement, then is it possible to add more than one line of code for execution like so? Is it more correct to use ifthen and switch statements for multiple lines of code?
(x == y) ? (alert("yo!"), document.write("woot!")) : (alert("meh!"), document.write("blah!"));
Conditional operators are intentionally succinct and especially useful for assignments:
var a = x ? 1 : 2;
Using them to conditionally run functions, while possible, should, for the sake of readability be done using IF/ELSE statements:
// This is possible but IMO not best practice:
X ? doSomething() : doSomethingElse();
While long-winded, most of the time, this is the better solution:
if (X) {
doSomething();
} else {
doSomethingElse();
}
One notable benefit to the IF/ELSE structure is that you can add additional tasks under each condition with minimal hassle.
Your last snippet is also possible but it looks somewhat long-winded and, again, might be better suited to a more conventional logical structure; like an IF/ELSE block.
That said, a conditional operator can still be readable, e.g.
(something && somethingElse > 2) ?
doSomeLongFunctionName()
: doSomeOtherLongFunctionName();
In the end, like many things, it's down to personal preference. Always remember that the code you're writing is not just for you; other developers might have to wade through it in the future; try and make it as readable as possible.
JavaScript won't prevent you from doing it, but it's very a unusual practice that will confuse anyone reading your code.
The conditional operator is almost always used for selecting two alternative values, not statements. An if statement is preferred for conditional branching of statements.
As to your last question, yes, if you really must, you can abuse the [] construct:
(x == y) ? [alert("yo!"), document.write("woot!")] : otherstuff();
But please don't. 8-)
It is entirely up to you, you can do it either way. You just have to ask yourself, though, does this style follow company guidelines, and how readable do you want this code to be?
Using if statements is way more readable.
Personally, I only use the ternary operator for simple and quick true/false conditions--where doing so makes sense--or where I need something "inline".
Either of the two methods are acceptable although you could have also written:
alert((x == y) ? "yo!" : "meh!");
Aside from that I would never recommend using an inline conditional for multiline statements, just use a standard if/else block. Given the syntax you entered isn't valid JS either, you could have placed the multiple statements into anonymous methods and yada yada, then you enter into a tangled mess of nearly unmanageable and unnecessarily difficult code. So again, standard if/else.
I agree with both Chris and J-P that:
Conditional operators are handy for short statements. J-P's variable assignment is a great example: var a = x ? 1 : 2;
Multi-statement clauses should be separated on to separate lines, for readability.
Conditional operators can be made readable as multiline statements with the right indentation, but if/else syntax is much more familiar to most developers. Readability is about matching the expectations of your reader, so familiarity is important.
I will add that multi-line conditional operators leave you open to semicolon insertion errors. Check out the JSLint documentation (see the section on "Line Breaking") for more on this. If you must use a multi-line conditional operator, make sure operators are at the end of each line. I would rework J-P's multi-line example thusly:
(something && somethingElse > 2) ?
doSomeLongFunctionName() :
doSomeOtherLongFunctionName();
As has been mentioned, there are many style guides out there and you can choose whichever you prefer. Some choices are more error-prone than others, however. Definitely take a close look at that JSLint documentation; it is a very well-thought-out style guide, and if you adhere to it, you can even use the JSLint tool to automatically check your code for potential problems.

Categories