Conditional returns or single variable return - javascript

I was wondering what if there's any preferable way of returning values conditionally.
And if not, if it makes any different for minified Javascript.
I was checking Google Javascript Style guide and they don't name anything about the issue. Same with Airbnb guidelines
Using multiple conditional returns:
function demo(x, y) {
if (x < y) {
return getX();
} else if (x === y) {
return 'equal';
}
return getY();
}
Or having a single return with and making use of a variable:
function demo(x, y) {
var position;
if (x < y) {
position = getX();
} else if (x === y) {
position = 'equal';
} else {
position = getY();
}
return position;
}

The following is a definition of the return statement.
The return statement stops the execution of a function and returns a value from that function.
Clearly, a conditional return would have to include a condition before that return.
About preference, as the word implies, it is really is a matter of habits.
If you're used to one syntax from, say, another language, or you just like it better - use it, as it will probably reduce syntax errors.
About minifying the code, I believe the switch/case syntax best works for simple conditions, and the ternary operation fits well for two-side conditions.
(Caution! philosophical paragraph ahead)
But again, in the end, the important thing, I believe, is that you will understand the code you write. And, with experience, you will find what suits you best.

You should concern yourself with readability. Write the code in the way that is most understandable. If you are returning from with a deeply nest condition that is likely to be confusing to future read, but if you have a series of early exits at the start of your function that is likely to make the code more readable (when compared to deep nesting). Break up big functions and leave mangling to the compilers. The Closure Compiler will produce the same results for either of your examples.
Examples:
http://closure-compiler.appspot.com/home#code%3D%252F%252F%2520%253D%253DClosureCompiler%253D%253D%250A%252F%252F%2520%2540compilation_level%2520SIMPLE_OPTIMIZATIONS%250A%252F%252F%2520%2540output_file_name%2520default.js%250A%252F%252F%2520%2540formatting%2520pretty_print%250A%252F%252F%2520%253D%253D%252FClosureCompiler%253D%253D%250A%250Afunction%2520demo1(x%252C%2520y)%2520%257B%250A%2520%2520%2520%2520var%2520position%253B%250A%250A%2520%2520%2520%2520if%2520(x%2520%253C%2520y)%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520position%2520%253D%2520getX()%253B%250A%2520%2520%2520%2520%257D%2520else%2520if%2520(x%2520%253D%253D%253D%2520y)%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520position%2520%253D%2520'equal'%253B%250A%2520%2520%2520%2520%257D%2520else%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520position%2520%253D%2520getY()%253B%250A%2520%2520%2520%2520%257D%250A%2520%2520%2520%2520return%2520position%253B%250A%257D%250A%250Afunction%2520demo2(x%252C%2520y)%2520%257B%250A%2520%2520%2520if%2520(x%2520%253C%2520y)%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520return%2520getX()%253B%250A%2520%2520%2520%257D%2520else%2520if%2520(x%2520%253D%253D%253D%2520y)%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520return%2520'equal'%253B%250A%2520%2520%2520%257D%250A%2520%2520%2520return%2520getY()%253B%250A%257D

Use ternary/conditional operator.
In Javascript conditional operator can evaluate to an expression, not just a statement.
var result = x < y ? getX() : getY();
This starts evaluation from left and if your condition meets criteria then result will be the first portion otherwise result would be second portion.
For more conditions you have to use it like
var result = IsYoungerThan18 ? serveMilk() : IsYoungerThan21 ? serveBear() : serveWine();
So in your case it should be
var result = (x < y) ? getX() : (x == y) ? "equal" : getY();

Related

Can you use the return statement as an expression

Recently I've discovered that sometimes it is more convenient to
condition && fnWhenTrue()
than
if(condition){
fnWhenTrue()
}
Honestly I've fallen in love with it and started using it wherever it makes sense. However I am unable to do a
condition && return
As return is a statement and not an expression. Is there a way around this or is this just not a valid use case and I should stop.
I am able to achieve similar effect using an inline if like this
if(condition) return
But it's just not the same.
Thanks in advance
No, return is not an expression in JavaScript / ECMAScript at the time of this answer.
If you don't want to write several lines / lengthy code, you can use the short if notation:
if (condition) return;
Which is only 2 characters longer than condition && return;!
Just as a reminder, val = exp1 && exp2 is an expression that is roughly equivalent to:
let val;
if (exp1) {
val = exp2;
} else {
val = exp1;
}
(with some main difference that exp1 is evaluated 2 times in my above example, whereas only once in exp1 && exp2).
Just as something to convince you that is probably intended in JS language design, as you can do:
let someResult;
// later...
someResult = exp1 && exp2;
then what would someResult got as value if you had a return instead of exp1 and exp2?

Using functions within conditionals

I've been doing Project Euler 'cause reasons. At one point, I found myself wanting to compare a value with the result of a function. I've since selected a different solution, but it's left me curious. How would this work? If I were to do something like:
//javascript
if x == mathyFunction(10){
//do this
}
function mathyFunction(y){
if(y>0){
mathyFunction(y-1);
return y;
} else {
return y;
}
}
I'm aware that this isn't tail recursive or anything fancy, I'm mostly just curious how the logic works behind this.
How would the computer interpret it? Would it return true if any one of the values == x or would it only return true if all the values == x. Experimentation has left me guessing, though I'll keep digging.
You didn't specify which language you're asking about, but assuming the usual semantics of the return statement, a function can't return more than once. So if by "return all values individually", you meant something like this:
function f() {
return 1;
return 2;
...
}
Then everything after the first return is just dead code and it's the same as if the return 1 was the only return statement in there. A function call has exactly one value and that value is what you're comparing against.
If you meant that mathyFunction returns a list or other collection object, which contains the values between x and y, then x will be compared to that list. Again the semantics will depend on the language, but in most languages comparing a number to a list will either be a type error or just false.
If by returning the values individually you were referring to a yield statement (like C#'s yield return or Python's yield), then the result of the function is going to be some kind of iterator object and the same thing as in the previous paragraph applies.

meaning of the AND operator in this line

What is the meaning of && in this JavaScript code?
function doIt(a) {
var b = a.parents(),
c = 1;
return b.each(function() {
jQuery(this).hasClass("free-content") && (c = 0)
}), c
}
normally I would think this would be a logical operator AND, but I can't figure out what it does in this line.
The logical AND operator in this case is used in place of an IF-statement. It will set c to 0 if jQuery(this).hasClass("free-content") returns a truthy value.
It's equivalent to:
if (jQuery(this).hasClass("free-content")) {
c = 0;
}
You wondering what it means is actually the reason I dislike this type of coding and consider it a bad practice. It's hard to read and can create confusion, which in turn can create bugs.
It's also worth noting what logical AND returns, if you want to use the returned value:
(Logical AND) Returns expr1 if it can be converted to false; otherwise, returns expr2. Thus, when used with Boolean values, && returns true if both operands are true; otherwise, returns false.
Here's an example showing, in my opinion, bad code since it's hard to follow:
var foo = bar && baz || qux;
The above code is equivalent to:
var foo;
if (bar && baz) {
foo = baz;
} else {
foo = qux;
}
Summary: Do not use logical operators as a nifty way to replace IF-statements or to save keystrokes. It will most likely come back and bite you or someone else in the ass.
I know there will be people arguing against me on this (usually the same people who doesn't want to use semicolon because ASI), but it's just a really bad idea to write code that only an expert would understand. There's no argument against it.
return b.each(function () {
jQuery(this).hasClass("free-content") && (c = 0)
}), c
b.each loops over all entries in b, and checks whether the current element has the class free-content set. Only if that yields true, the second part of the expression is evaluated – because they are concatenated via &&, and JavaScript stops evaluating such an expression when the first part is false, because then the whole expression can’t become true any more.
So if there is an element with that class, the second part is evaluated – and thereby c is set to the value 0 (because that’s the assignment operator = there, and not a comparison).
And after that each loop is finished, the value of c is returned to the outside – because each() and c are connected via the comma operator , here, which evaluates its operands from left to right and then “returns” the second one.
JavaScript allows a lot of “fancy” coding like this, but as Marcus already said in his answer, that is hard to read, and there is no actual advantage here over, say
b.each(function() {
if(jQuery(this).hasClass("free-content")) {
c = 0;
return false;
}
});
return c;
I added the return false here inside the each loop, because once we found an element with that class, we don’t need to search the rest of them any more – that’s something that the person who came up with the original code forgot to do in all their “fancy-ness” … their loop will continue to iterate over all of b’s elements, not matter if it finds the element it is looking for in the first round already.

Why does javascript accept commas in if statements?

I stumbled across some javascript syntax that seemed like it should produce a parse error of some kind but doesn't:
if (true, true) {console.log('splendid')} else {console.log('horrid')} // splendid
if (true, false) {console.log('splendid')} else {console.log('horrid')} // horrid
It seems only the last expression affects the logic, though all expressions are executed:
if (console.log('super'), true) {console.log('splendid')} // super splendid
Anyone know why that is valid javascript syntax? Is there any practical use for it?
The comma operator chains multiple expressions together, and the result of the operation is the value of the last operand. The only real use for it is when you need multiple side effects to occur, such as assignment or function calls.
The comma operator evaluates each of its operands (from left to right)
and returns the value of the last operand.
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/Comma_Operator
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Expressions_and_Operators#comma_operator
commas in javascript are actually pretty arcane. The coolest use I have seen is this
while(doSomething(), checkIfSomethingHappened());
the most common would be the way var is used in modern js
var foo = 1,
bar = 2;
This is also the same as in most other programming languages where you might have multiple iterators in a loop.
int x,y;
for(x = 0, y = 0; x < 10 || y < 100; x++, y++) {
....
}
I permits to do operations and comparisons in the same context.
Example:
if(a = 2, a > 1) console.log('a is', a)

In Javascript, what's better than try/catch for exiting an outer scope?

In Javascript, I sometimes want to return a value from a scope that isn't the current function. It might be a block of code within the function, or it might be an enclosing function as in the following example, which uses a local function to recursively search for something. As soon as it finds a solution, the search is done and the outer function should just exit. Unfortunately, I can't think of a simpler way to do this than by hacking try/catch for the purpose:
function solve(searchSpace) {
var search = function (stuff) {
var solution = isItSolved(stuff);
if (solution) {
throw solution;
} else {
search(narrowThisWay(stuff));
search(narrowThatWay(stuff));
};
};
try {
return search(searchSpace);
} catch (solution) {
return solution;
};
};
I realize one could assign the solution to a local variable and then check it before making another recursive call, but my question is specifically about transfer of control. Is there a better way than the above? Perhaps involving label/break?
Edit: since the answers to date are variations of "ew that's bad
you're not supposed to do that", let me add some necessary
context. I'm hacking on an open-source compiler that targets
Javascript. No one is going to write this code by hand, so please
don't tell me "this is a bad programming technique". What I want is a
better code generation technique. The question is whether anyone has
any clever hack for exploiting Javascript to get more flexible control
transfer.
The reason assigning the result to a local variable and checking it is
ruled out is because that requires understanding the code in a way
that is hard for a compiler to do.
It seems I stand corrected on the intent of the question. If statements are are a useful and readable way to structure code and make it flow however you want to. There's a reason goto was taken out of so many languages, because you don't need it. And it seems like, based on your example code, you're using a try-catch block as a form of goto. If you don't want certain things to run then use if statements or equivalents:
function solve(searchSpace) {
function search = function (stuff) {
//|| will only return the first value if that value is truthy, subsequent values will be ignored
return isItSolved(stuff) || (search(narrowThisWay(stuff)) || search(narrowThatWay(stuff)));
};
return search(searchSpace);
};
I know of no way to break out of function calls like you want. You can break out of loops using labels, but it doesn't seem that's much help to your situation. Other than that, I don't think JavaScript has any such ability beyond your use of exceptions
function solve(stuff) {
return isItSolved(stuff) || solve(narrowThisWay(stuff)) || solve(narrowThatWay(stuff));
}
Bob's way is good... exept that he uses twice the function statement (and that he uses ; after a function delaration without an assignment)... and that as we can do it that way, function solve actually is function search.
PS : This code will epically fail if the isItSolved, narrowThisWay or narrowThatWay functions can return a value evaluated to false as a positive result. In this cas, you would have to use ? : statement in order to check if all responses are !== undefined.
PS2: And of ourse, if these function can send an error, you have to catch it...
It looks like you're doing a fairly straightforward recursive search in your example. Why not just use "return"?
function solve(searchSpace) {
var search = function (stuff) {
var solution = isItSolved(stuff);
if (solution) {
return solution;
} else {
solution = search(narrowThisWay(stuff));
if (solution) {
return solution;
}
return search(narrowThatWay(stuff));
};
};
return search(searchSpace);
};
I suppose it could be that there are other constraints you haven't mentioned, but it's in general possible to turn any control flow into a set of nested (or recursive) functions, with appropriate return values.
The cleanest way would be to use a continuation, but you don't have that efficiently in JS (a few JS engines support continuations, but for the rest there's only CPS, which cries out for tail calls). In C, you could use setjmp/longjmp. In Common Lisp, you could use conditions (which include the functionality of exceptions plus much more). In JS, exceptions are the only non-local control flow option you have available.
You can programmatically transform a program into another that uses CPS.
function solve(searchSpace, isItSolved, isBase, narrowThisWay, narrowThatWay) {
function search(stuff, k) {
solution = isItSolved(stuff);
if (solution) {
return solution;
} else if (isBase(stuff)) {
return k();
} else {
return search(narrowThisWay(stuff), function() {
return search(narrowThatWay(stuff), k);
});
};
};
return search(searchSpace, function(val) {return val});
};
var arr=[1, 2,9,72,0,34,5,33,24,62,89,90,30,54,590,23,59,62,73];
solve(arr, function(a) {return (a.length==1 && a[0] == 5) ? a[0] : false;},
function (a) {return a.length < 2; },
function (a) {return a.slice(0, a.length / 2);},
function (a) {return a.slice(a.length / 2);}
);

Categories