loverajoel / jstips

This is about useful JS tips!
http://jstips.co
GNU General Public License v3.0
12.5k stars 803 forks source link

Genius Behaviour of Logical Operators #356

Closed cerlestes closed 8 years ago

cerlestes commented 8 years ago

First, awesome repository; I love it!

Now my suggestion for another tip: In my opinion, one of the best things about JS is the lax and kind of lazy way in which the logical operators || (logical OR, "default operator") and && (logical AND, "guard operator") work. Basically: OR will yield the left value if truthy, otherwise the right value; AND will yield the right value if both are truthy, otherwise the falsy is yielded. So to say: the value that matters, that makes or breaks the comparison, is yielded. They don't convert to boolean, because of JS's inherent way of handling truthy/falsy values. You can use them in some very fantastic ways. This would make for a very interesting article.

console.log( 0 || 1 || 2 ); // 1 == true
console.log( 0 && 1 && 2 ); // 0 == false
console.log( "foobar" || "default value" ); // "foobar" == true
console.log( null || "default value" ); // "default value" == true
console.log( false && "guarded value" ); // false == false
console.log( true && "guarded value" ); // "guarded value" == true
console.log( null || true && "guarded value" || null ); // "guarded value" == true
zenopopovici commented 8 years ago

@cerlestes Can you create a PR for it?

cerlestes commented 8 years ago

I'll see if I can come up with an interesting text. No promises though :)

FagnerMartinsBrack commented 8 years ago

Maybe we could improve this very good tip and add that it can be useful mostly to make sure your code is by any means impossible to read.

Jokes aside, there are cases where this might be useful though, let's say you are maintaining a library and you want the code to be better gzipped by suppressing unnecessary indexed substring such as : and ? for ternary operators that are not being used anywhere else.

Other than that, there is no real value for this in most real world applications, the only use is if the developer wants to feel smart or turn the code impossible to read.

cerlestes commented 8 years ago

@FagnerMartinsBrack I disagree. It's not widely used apart from the following case, but there it makes a lot of sense to use the operators in that way and even increases readability dramatically:

{
    url: options.url,
    data: options.data || fallback || {},
    type: options.type || 'text/json'
}

I agree that it can be used in wrong ways, but the tip should of course also explain that. The wrong way to use this is to execute code, just like with the ternary operator:

// Bad use of logical AND:
shouldExecute && execute();

// Bad use of ternary operator:
(shouldExecute) ? execute() : foobar();

// Correct way: use if-construct to execute code
if (shouldExecute) {
    execute();
}

Please note that I didn't want to include my examples from the original post in the tip, as they're purely for demonstrating how it works. Of course no actualy production code should look like them.

FagnerMartinsBrack commented 8 years ago

It's not widely used apart from the following case, but there it makes a lot of sense to use the operators in that way and even increases readability dramatically:

You got me with the logical OR. This one is widely used for default values in ES5:

var item = items[index] || {}

Although in ES6 with default parameters the idea is that it starts to be used less often.

Your second example clearly explain what I mean, using logical operators for cases where a regular imperative if would make much more sense.

hnaoto commented 8 years ago

@cerlestes = D Thanks for the interesting post

Although I don't have much knowledge of gzipping and bad use of ternary operator mentioned in previous comments, I have a use case of ternary operator. Please bear with me if it turns out to be bad practice : )

Some really simple if statement could be saved by using ternary operator

We could have

var count = i % 2 ? 6 : 7;

instead of

....
if (i % 2 == 0) {
  count = 7;
} else{
  count = 6;
}

//or something like this 
var count = 6;
if (i % 2 == 0) {
  count = 7;
} 

In this case, I was rendering a "hive" with several lines of hexagons. The odd number of line has 6 hexagons each line, and the even one has 7.

Similarly, another example:

// var data = [...{"Hero":"valla","ID":"'14' ","Free":true}...]

var overlay =  data[i]['Free'] ?  $('<div class="overlay free" >   </div> ')  
:  $('<div class="overlay" >  </div> ');

If Free is true, .free class will be added to the element.

Personally, logical operators are readable unless they are overly used. Furthermore, considering about the spacing issue of squeezing several really simple if statements into for loops, logical operator might be a better approach in my opinion.

Hope this helps! Thanks for your patience. ^ ^

ghost commented 8 years ago

Personally, logical operators are readable unless they are overly used. Furthermore, considering about the spacing issue of squeezing several really simple if statements into for loops, logical operator might be a better approach in my opinion.

const metaPool = [
    ['sane_gentoo_commands','Sane Gentoo Commands','May 7, 2016','Gentoo'],
    ['sane_gentoo_repo','Sane Gentoo Repository','May 8, 2016',''],
    ['random_post3','Random post 3','May 19, 2016',''],
    ['run', 'Decreased the blog loading time - again', 'May 26, 2016', ''],
    ['back_to_the_future', 'Back to the future', 'May 31, 2016', ''],
    ['jblogfy', 'My 3rd static generator', 'June 01, 2016', ''],
    ['templating','Learn to love the ES6 native templating','June 04, 2016','JavaScript,ES6,ESNEXT']
];

const capFirst = text => text.charAt(0).toUpperCase() +
            text.slice(1).toLowerCase();

const postTemplate = arr => `
    <div id='progress-bar' class='hide'>
        <div id='cur-progress' class='progress-bar'></div>
    </div>
    <article class='details-wrapper'>
        <div class='details-head'>
            <div class='details-head-wrapper'>
                <h6 id='details-title'></h6>
                ${arr[1][0] ?
                    `<span>
                        Post Categories:\u00A0
                    </span>` : '' }
                ${arr[1][0] ?
                    arr[1].map(curCat => `
                        <a href='#!category=${curCat}'>
                            <span class='label label-default'>
                                ${capFirst(curCat)}
                            </span>
                        </a>\u00A0 
                    `).join('') : ''}
            </div>
        </div>
        <div id='details-body'>
        </div>
        <div class='details-footer'>
            <hr>
            <span>
                The source file for this entry can be found <a id='md-src'>Here</a>
            </span>
        </div>
        <ul class='pager'>
            ${metaPool[arr[0] - 1] ? `
                <li class='previous'>
                    <a href='#!post=${metaPool[arr[0]-1][0]}'>
                        &larr; Older
                    </a>
                </li>
            `: ''}
            ${metaPool[arr[0] + 1] ? `
                <li class='next'>
                    <a href='#!post=${metaPool[arr[0]+1][0]}'>
                        Newer &rarr;
                    </a>
                </li>
            ` : ''}
        </ul>
    </article>
`;
console.log(postTemplate([
    1,
    metaPool[1][3].toLowerCase().split(',')
]));
console.log(postTemplate([
    6,
    metaPool[6][3].toLowerCase().split(',')
]));
FagnerMartinsBrack commented 8 years ago

@wifiextender Is that unmaintainable code you posted supposed to prove something?

kurtextrem commented 8 years ago

I can perfectly understand what's going on. Comments would be nice though (but not sure where to integrate them)

FagnerMartinsBrack commented 8 years ago

Comments would be nice though

There is also the alternative of trying to write legible code instead of writing comments to workaround it.

kurtextrem commented 8 years ago

What exactly isn't understandable? Even through my gmail app I could read the code, with syntax highlighting it's even easier.

FagnerMartinsBrack commented 8 years ago

What exactly isn't understandable? Even through my gmail app I could read the code, with syntax highlighting it's even easier.

Code should be self documented. If the developer judges that a code need comments (as you proposed), then that is a strong indication that the code is not legible enough. If it was legible enough, then the need for comments explaining what the code is doing would be unnecessary.

ghost commented 8 years ago

@FagnerMartinsBrack Your <= ES5 and readable alternative without using templating engine is ?

const titlenLinkTemplate = arr => `
     ${arr.map(el => `
        <h4 class='entry-title'>
            <a href='${el[0]}' class='snippet-title text-muted'>
                ${capFirst(el[1])}
            </a>
        </h4>
        <hr>
        `).join('')
     }
`;

console.log(titlenLinkTemplate(metaPool));

@kurtextrem Thanks for the support.

FagnerMartinsBrack commented 8 years ago

@wifiextender

@FagnerMartinsBrack Your <= ES5 and readable alternative without using templating engine is ?

Using a proper view framework like React without trying to reinvent the wheel with plain JavaScript.

I am not saying that isn't a smart approach for templating, it is, but there are two kinds of smart solutions: The one that want to focus in being smart and focus in irrelevant performance at the cost of legibility, and the one that focus in solving problems with proper legibility at the cost of irrelevant performance.

Nobody can provide an alternative for that code because it is impossible to know what it is trying to achieve, and the code itself cannot express clearly what it is trying to do. Architecture starts from the problem and goes up to the implementation, without knowing the problem it is impossible to suggest a different implementation, infering the problem from a snippet of code is dangerous, for that we might be missing important context. I can bet though that the problem could be fixed without having to create tons of arr[1][0], [arr[0] - 1], : '' } and [6][3].toLowerCase().split(',') tokens that mean nothing to the reader.

JavaScript is not there yet, unfortunately.

ghost commented 8 years ago

Nobody can provide an alternative for that code because it is impossible to know what it is trying to achieve, and the code itself cannot express clearly what it is trying to do.

Same could be said for your statement:

Using a proper view framework like React without trying to reinvent the wheel with plain JavaScript.

When you complete porting my last and short example to ES5, you'll build the confidence that you can solve my first example. Upon trying it, you'll start losing track what's going on, the code will become more unreadable and you'll end up frustrated yelling at your screen. Then you'll realize what's the big deal of templating languages engines and frameworks such as React you mentioned earlier in the <= ES5 era.


I'll butt out of this conversation for now, until some close-minded and moaning people are gone as they brought here only negativism without contributing back something useful.

FagnerMartinsBrack commented 8 years ago

When you complete porting my last and short example to ES5, you'll build the confidence that you can solve my first example.

I never said anything about ES5, you did.

Upon trying it, you'll start losing track what's going on, the code will become more unreadable and you'll end up frustrated yelling at your screen.

Lots of assumptions and argument against a straw man that you created about this ES5 thing that I never mentioned.

Then you'll realize what's the big deal of templating languages engines and frameworks such as React you mentioned earlier in the <= ES5 era.

I never mentioned it is better to use React in <= ES5 era, I mentioned it is better using React, it doesn't matter which ES version. My arguments were against the way variables in the template were structured using plain JS, not about using the concept of templates or ES5/ES6/etc.. The code is just reinventing React and all implicit scope features (that cannot be seen in the code) unnecessarily, doing such thing take time and resources of any project.

TL;DR:

  1. This comment have shown a piece of code without any explanation on what it has to do with this post.
  2. This comment thinks that understanding what a trivial piece of code does is enough to conclude that it has an efficient architecture.
  3. This comment started a parallel off-topic conversation with a few statements of fact.
  4. This comment started a discussion against itself totally ignoring or not understanding the previous comment.
zenopopovici commented 8 years ago

Please come back with a PR. We can debate when we review it.