4

I am using && like this and it works

typeof foo === 'function' && foo(); //if foo exist then call it

instead of

if (typeof foo === 'function') { foo(); }

Is it wrong to do or just a matter of style and taste? For me it is natural and I want to use &&, but now a linter complained: Expected an assignment or function call and instead saw an expression.

Can there be any real issues here or is it just a matter of convention?


Here is a code snippet:

function foo(x) {
  console.log("foo say " + x)
}

function bar(x) {
  console.log("bar say " + x)
}

let s = "OK"

typeof foo === 'function' && foo(s)

if (typeof bar === 'function') bar(s)

/*
   Function noo() does not exist.
   Error to try call it is prevented by the check.
   noo && noo() is not enough, so typeof is a must! 
*/
typeof noo === 'function' && noo()

console.log("OK so far")

Notes

  • To clarify my purpose was to use && as a check for existence (declared and defined).
  • If the left hand side of && fails the right hand side will not execute
  • It is useful in return and assignments too, but if is not. If an else-part is wanted, then use ?. The then else parts has to return same type.
  • I missed typeof at first and have corrected but see in comments it miss. Maybe common mistake or just easy writing while we all show understanding. But to be correct (i think) - the only way to check existence is with typeof, instanceof or try except window things you can do for example history && history.back().
  • try { foo(); }; catch (e) {}; can be used. At least one catch clause, or a finally clause, must be present.
  • if (a()) {b(); c()} equals a() && (b(), c()) because functions can be both in statements and expressions. Use comma operator.
  • The extreme is that the function is not declared and the other extreme is when function has already returned a value x = x || foo() it need not to return again (that is called memoization of a deterministic function)
  • 2
    Maybe `return foo && foo();` – Ele Jun 18 '19 at 16:19
  • It is already returning to void –  Jun 18 '19 at 16:20
  • Please post a verifiable example (Code snipper). – Ele Jun 18 '19 at 16:21
  • 1
    There will be no differences in behavior. – 4castle Jun 18 '19 at 16:21
  • If I put return in front the next row will not execute –  Jun 18 '19 at 16:22
  • Is there any linter that does not consider coding convention? –  Jun 18 '19 at 16:47
  • @PauliSudarshanTerho you can configure any linter I know of to suit your needs. Well, aside from JSLint, I suppose - it's probably the oldest (for JS) and the most opinionated. Everything else spawned off in order to give programmers *more* control. Which linter do you use that cannot be configured? – VLAZ Jun 18 '19 at 16:54
  • Code snippet included. Now we see any return in front would give error. –  Jun 18 '19 at 16:54
  • Beautifytools online linter do it direct on paste so I dont need to scroll for a button to start linting. Ignoring ';' check would be fine also. And I want it online so I know where I have it. –  Jun 18 '19 at 16:57
  • ...why aren't you using a linter that your editor most likely has integration for? It would check code *as you type* to give you immediate feedback. – VLAZ Jun 18 '19 at 16:59
  • VERY good idéa.. but.. Ehm, I am using Geany editor on my Raspberry Pi. Does not have: https://plugins.geany.org –  Jun 18 '19 at 17:02
  • I hope @4castle have the best comment: There will be no differences in behavior –  Jun 18 '19 at 17:06
  • @PauliSudarshanTerho [here is an article about setting up JSHint in Geany](https://github.com/trongthanh/geany-for-front-end-dev/wiki/Using-JSHint-with-Geany). I imagine ESLint and others will just be added the same way. – VLAZ Jun 18 '19 at 17:14
  • Good, i'll try! I like RPI to performance code small projects on a large screen. Feels safe to believe it can not fit any built in spyware - afraid of M$ copy paste programmers. And Geany it has. –  Jun 18 '19 at 17:36
  • **Edits I have made**: Missed `typeof`, added code snippet, `==` should be `===`, added point about `return` so @Ele and upvoters is not too wrong, but it miss typeof because I did first. –  Jun 18 '19 at 19:07
  • 1
    MDN says "At least one catch clause, or a finally clause, must be present." Just like your chrome console. Also, while the if can be avoided here (though you never know maybe one day you'll want to add more logic in the case it's defined, or an else block), semi-colons are cheap. You are asking for trouble not using it. – Kaiido Jun 22 '19 at 00:21
  • Yes.. as another said.. the point of try is to catch errors, not hiding it, that would be very bad programming. –  Jun 23 '19 at 11:00

3 Answers3

2

The linter's job is to look out for things that while syntactically valid may not follow recommended best practices.

"Expected an assignment or function call" probably means that it expects foo to be foo() or foo = in the first part, that seeing it without a call is, in its opinion, a mistake.

You're free to do whatever you want. Short-circuit evaluation behaves predictably, so it's going to work, but it may be confusing to people unfamiliar with that style.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • The syntactic sugar makes the call more dependable and readable in the long run. Of course if the JavaScript is for a one off ad that doesn't matter. – Michael Shopsin Jun 18 '19 at 16:24
  • 2
    @MichaelShopsin Exactly, it depends what you're optimizing towards. Readability? Accessibility to new coders? Minimal code size / fewest lines? Antagonizing the linter as much as possible? – tadman Jun 18 '19 at 16:25
  • 4
    "Antagonizing the linter as much as possible" hahaha – Jorge Fuentes González Jun 18 '19 at 16:31
  • 1
    "*Short-circuit evaluation behaves predictably, so it's going to work, but it may be confusing to people unfamiliar with that style.*" I personally really dislike these satatements because I have to read the line to the end to see that it's actually a conditional statement, then go back to the beginning and re-read it to see what the condition is, in order to understand what is being conditionally executed and why. So it induces a sideways [yo-yo problem](https://en.wikipedia.org/wiki/Yo-yo_problem). – VLAZ Jun 18 '19 at 16:40
  • @VLAZ I agree with that sentiment, especially when it comes to languages with the trailing `if` operator, but if the first part of this is short enough to fit in <20 characters it's usually obvious at a glance what it does. – tadman Jun 18 '19 at 16:42
  • 1
    @tadman I agree on the shortness part. Still, then you run into inconsistencies when you have `doIt && doStuff()` and something along the lines of `if (stuffCheckerManager.letMeCheckIfStuffHasToBeDoneOrNot()) doStuff()` I'm only slightly exaggarating but you can get very long conditions. Worse, you might later have to refactor them and add more conditions - the short one might have to turn into `(doIt || forceDo) && doStuff()` which is not as legible any more and any further condition added best make that into a normal `if`. Whatever the case, the inconsistency between checks is annoying. – VLAZ Jun 18 '19 at 16:52
  • Otherwise checking with try `{ foo() }` could be fine to do `foo()` if it exists, but the `catch(e){}` afterwards is syntactic bs. Chrome say *Uncaught SyntaxError: Missing catch or finally after try* but MDN say it is an optional syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#Syntax –  Jun 21 '19 at 23:02
  • The whole point of `try` is `catch` so without a `catch` it's not valid. You can't just be like "hey, run this code and ignore errors", that's "Pokémon Exception Handling" (catch them all!) which is an anti-pattern. – tadman Jun 22 '19 at 12:32
2

In the background there is a bit more with the && thingy, but for 99% of the cases it is perfectly fine to do it that way, just like this one.

Now as a personal opinion, for a one-liner I prefer it the && way instead of the if one, because I can't stand if keyword without a block below it hehe.

If you know what you are doing, linters are too picky sometimes.

Jorge Fuentes González
  • 11,568
  • 4
  • 44
  • 64
  • 4
    What is the 1% of cases you're referring to? – 4castle Jun 18 '19 at 16:23
  • @4castle Well, knowing that one also returns the value of `foo()` and the other don't, there are differences between them. I can't tell an exact case right now but having differences between them I'm sure that there's a case where, as a newbie, `&&` don't fit what you expect and you will need an `if` (or an `||` or whatever). The point I wanted to show is that there are slight differences between them so don't stick that they are exactly the same. I guess I expressed it a bit weird. – Jorge Fuentes González Jun 18 '19 at 16:27
  • Maybe you want to run two functions if one of them exists, something like `if (foo) { foo(); bar() }`, which maybe will work with `foo && foo() && bar()` depending of `foo()` result. And using `foo && foo(); bar();` will not work as you may think, as `bar()` will be always executed. These are newbie faults if you are trying to convert an `if` to a one-liner with `&&` (or whatever operator). – Jorge Fuentes González Jun 18 '19 at 16:29
  • `a() ; b()` is neither `a() && b()` or `a() || b()` because `b()` evaluates no matter of `a()` be falsy or not –  Jun 20 '19 at 21:19
  • Can also say `a() ; b()` is `a() && b()` if they are guaranteed to always return something. Or say `a() ; b()` is `a() || b()` if they are restricted to always return a falsy value. –  Jun 21 '19 at 12:03
  • @PauliSudarshanTerho Yep exactly. That's why coverting `if(a) { a(); b();}` to a one-liner can be tricky for newbies. I can see someone converting it to `a && a(); b();` for sure xD – Jorge Fuentes González Jun 21 '19 at 14:28
  • `&&` binds harder than `||` and `;` the least. Maybe teachers don't tell because it feels obvious as `+` and `-` precedense for them or it belongs to a course in compiler construction? But the essentials is important for beginners. –  Jun 23 '19 at 11:06
  • Not many know that `if(a) { a(); b();}` can be converted to `a && (a(), b())` and maybe shouldn't. –  Jun 23 '19 at 11:17
  • The 1% of the cases where it can go wrong is when you try to be clever and make it part of a larger expression where the result of `foo && foo.bar()` value is (unexpectedly) used, probably due to not quite understanding the semantics of the `&&` operator – Burak Jun 25 '19 at 10:19
  • @Burak yep, exactly. – Jorge Fuentes González Aug 18 '22 at 12:21
1

You could use a void operator. This evaluates the expression and returns undefined.

void (foo && foo());

var foo;

void (foo && foo());

foo = () => console.log('foo');

void (foo && foo());
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Not useful when my purpose is to check if foo exists –  Jun 21 '19 at 10:09
  • void works. It gives alway undefined and foo() never evaluate. I want to evaluate foo() if it is defined. –  Jun 21 '19 at 10:12
  • 1
    right, you need some parentheses, otherwise only the first operand is evaluated. – Nina Scholz Jun 21 '19 at 10:36
  • Without `var foo` I got the error *foo is not defined*, but more correct would have been to say *foo is not declared* –  Jun 21 '19 at 22:30
  • `void (bar && bar());` does not work if I want to check if `bar` exists. Because it is not declared it give error. –  Jun 23 '19 at 10:54