Comparison Operators & Equality

  • 15.1 Use === and !== over == and !=. eslint: eqeqeq

  • 15.2 Conditional statements such as the if statement evaluate their expression using coercion with the ToBoolean abstract method and always follow these simple rules:

    • Objects evaluate to true
    • Undefined evaluates to false
    • Null evaluates to false
    • Booleans evaluate to the value of the boolean
    • Numbers evaluate to false if +0, -0, or NaN, otherwise true
    • Strings evaluate to false if an empty string '', otherwise true
    1. if ([0] && []) {
    2. // true
    3. // an array (even an empty one) is an object, objects will evaluate to true
    4. }

  • 15.3 Use shortcuts for booleans, but explicit comparisons for strings and numbers.

    1. // bad
    2. if (isValid === true) {
    3. // ...
    4. }
    5. // good
    6. if (isValid) {
    7. // ...
    8. }
    9. // bad
    10. if (name) {
    11. // ...
    12. }
    13. // good
    14. if (name !== '') {
    15. // ...
    16. }
    17. // bad
    18. if (collection.length) {
    19. // ...
    20. }
    21. // good
    22. if (collection.length > 0) {
    23. // ...
    24. }

  • 15.4 For more information see Truth Equality and JavaScript by Angus Croll.

  • 15.5 Use braces to create blocks in case and default clauses that contain lexical declarations (e.g. let, const, function, and class). eslint: no-case-declarations

    Why? Lexical declarations are visible in the entire switch block but only get initialized when assigned, which only happens when its case is reached. This causes problems when multiple case clauses attempt to define the same thing.

    1. // bad
    2. switch (foo) {
    3. case 1:
    4. let x = 1;
    5. break;
    6. case 2:
    7. const y = 2;
    8. break;
    9. case 3:
    10. function f() {
    11. // ...
    12. }
    13. break;
    14. default:
    15. class C {}
    16. }
    17. // good
    18. switch (foo) {
    19. case 1: {
    20. let x = 1;
    21. break;
    22. }
    23. case 2: {
    24. const y = 2;
    25. break;
    26. }
    27. case 3: {
    28. function f() {
    29. // ...
    30. }
    31. break;
    32. }
    33. case 4:
    34. bar();
    35. break;
    36. default: {
    37. class C {}
    38. }
    39. }

  • 15.6 Ternaries should not be nested and generally be single line expressions. eslint: no-nested-ternary

    1. // bad
    2. const foo = maybe1 > maybe2
    3. ? "bar"
    4. : value1 > value2 ? "baz" : null;
    5. // split into 2 separated ternary expressions
    6. const maybeNull = value1 > value2 ? 'baz' : null;
    7. // better
    8. const foo = maybe1 > maybe2
    9. ? 'bar'
    10. : maybeNull;
    11. // best
    12. const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

  • 15.7 Avoid unneeded ternary statements. eslint: no-unneeded-ternary

    1. // bad
    2. const foo = a ? a : b;
    3. const bar = c ? true : false;
    4. const baz = c ? false : true;
    5. // good
    6. const foo = a || b;
    7. const bar = !!c;
    8. const baz = !c;

  • 15.8 When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators (+, -, *, & /) since their precedence is broadly understood. eslint: no-mixed-operators

    Why? This improves readability and clarifies the developer’s intention.

    1. // bad
    2. const foo = a && b < 0 || c > 0 || d + 1 === 0;
    3. // bad
    4. const bar = a ** b - 5 % d;
    5. // bad
    6. // one may be confused into thinking (a || b) && c
    7. if (a || b && c) {
    8. return d;
    9. }
    10. // good
    11. const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
    12. // good
    13. const bar = (a ** b) - (5 % d);
    14. // good
    15. if (a || (b && c)) {
    16. return d;
    17. }
    18. // good
    19. const bar = a + b / c * d;