DEV Community

Shi Ling for Uilicious

Posted on • Originally published at uilicious.com

๐Ÿค”Pop Quiz! Which of these is an infinite loop?

Pop Quiz!

Which of these is an infinite loop?

And guess many times console.log will be printed.

A: let 5x3 Loop

for(let i = 0; i < 5; i++){
  for(let i = 0; i < 3; i++){
    console.log(1)
  }
}
Enter fullscreen mode Exit fullscreen mode

B: var 5x3 Loop

for(var i = 0; i < 5; i++){
  for(var i = 0; i < 3; i++){
    console.log(1)
  }
}
Enter fullscreen mode Exit fullscreen mode

C: var 5x5 Loop

for(var i = 0; i < 5; i++){
  for(var i = 0; i < 5; i++){ 
    console.log(1)
  }
}
Enter fullscreen mode Exit fullscreen mode

D: let 5x5 Loop

for(let i = 0; i < 5; i++){
  for(let i = 0; i < 5; i++){
    console.log(1)
  }
}
Enter fullscreen mode Exit fullscreen mode

Answer

Which of these is an infinite loop?
B: var 5x3

Guess many times console.log will be printed.
A: let 5x3 - 15 times
B: var 5x3 - Infinite times
C: var 5x5 - 5 times
D: let 5x5 - 25 times

Did any of the answers surprise you? I was!

๐Ÿง What, but why? The difference between let and var.

< flashback >


A junior dev came up to me for help with a bug in his Javascript code that's causing a memory error, my intuition told me that there was an infinite loop somewhere.

One of the nested loops stood out as a red flag to me:

for(let i = 0; i < 5; i++){
  for(let i = 0; i < 5; i++){
    // some code
  }
}
Enter fullscreen mode Exit fullscreen mode

I said - Hey, you're using the same variable name for outer and inner loop, this is going to cause an infinite loop.

JD: Huh? How come?

Me: Because the inner loop is reseting i to 0, causing the outer loop to never exit. Come let's try this in the console, it's faster to just see it.

To my surprise, there wasn't an infinite loop, we got this:

let 5x5 loop

console.log was printed only 25 times.

Me: Hm...? That's odd. (Then I realised that the code uses let instead of var.) Maybe... the infinite loop only happens if you use var instead of let.

We changed let to var, and ran it again:

var 5x5 loop

console.log was printed only 5 times.

Us: Wait whuut? Why it is printing only 5 times?

Me: ... Ahhh, that's because since this example uses var instead of let. let allows you to safely declare scoped variables with the same name. But when you use var to declare variables with the same name in different parent-child scopes, they all reference the same thing. So here, when you use var, the inner loop is sharing the same i as the outer loop. Meaning, when the inner loop counts i up to 5 and exits, the outer loop exits immediately because it's i counter is set to 5 by the inner loop.


JD: Ohh... okay, then what if we set the inner loop to exit when i is 3? I guess that produces an infinite loop?

Me: Let's find out.

var 5x3 loop

console.log was printed way beyond 15 times, and crashed the browser.

Us: ๐Ÿ™Œ We got an infinite loop!


And what if we change var to let now?

let 5x3 loop

console.log was printed only 15 times.


JD: Ok, cool. But what's the use case of var then?

Me: Ah that's a good question. ๐Ÿค” ...none actually. There isn't a good use case for var now that ES6 introduced let. var is just how we used to declare variables - but then there's this problem with variables leaking out of their scope - hence let was proposed. Don't ask me why they decided to name the keyword let. And this is why our eslint is configured to enforce let over var. :)

JD: Oh! Ok... so if this loop isn't causing the memory error, so what's causing it? (That turned out to be something else altogether.)

< /flashback >

Ah thinking back, we have it good now with ES6.

Top comments (18)

Collapse
 
chrisachard profile image
Chris Achard

Ha! That's a great illustration for why let is better than var :)

I actually did have a case where I had to use var once - but it was a really obscure hacky-case where I found myself actually wanting to leak the variable scope (because of how a third party library was behaving)... but! I quickly came to my senses and refactored the whole thing so I wouldn't have to do that.

I took more time to finish the feature - but I'm sure it was worth it ๐Ÿ˜…

Collapse
 
karataev profile image
Eugene Karataev

I still have one use case for var - a browser's developer tools.
Often I need to quickly test an idea and I write
var foo = 'bar'
then after some thoughts I press arrow up on keyboard and change previous input to
var foo = 'baz'

With let I get Uncaught SyntaxError: Identifier 'foo' has already been declared and it always frustrating ๐Ÿ˜

Collapse
 
shiling profile image
Shi Ling

Oh yea, I use var in console for the same reason!

Collapse
 
dpremus profile image
Danijel Premus

Fortunately my compiler will report an error If I try something like that.

I think that you should write some hint for beginners, that this kind variable naming is not good pratice and should be avoided.

Collapse
 
shiling profile image
Shi Ling

Oh yea I agree.

At the end, I reminded JD with this:

PSA: Use different names for counters in nested loops - to not only prevent errors, but helps in readability and clarity.

Not too sure if eslint helps with this, but I definitely wish there's an automatic way to catch it.

Collapse
 
dpremus profile image
Danijel Premus • Edited

I made same test with TypeScript and unfortunately this kind of code pass without warning. (:

After that I made same test with another language (Delphi) that Anders Hejlsberg (designer of TypeScript) designed 24 years ago and this old compiler report error. :)

I forgot to mention in my previous comment that your article is great.

Collapse
 
joshcheek profile image
Josh Cheek

This option looks promising: eslint.org/docs/rules/no-shadow

Thread Thread
 
shiling profile image
Shi Ling

Ahhhh yesss ๐Ÿ‘

Collapse
 
metruzanca profile image
Samuele Zanca

That 5x3 loop surprised me. But the breakdown of it makes sense.

These sort of challenges always make me laugh as they're really tricky sometimes meanwhile they're examples of what not to do when writing JavaScript. Anti-patterns to an extent. And the principal anti-pattern for js is var. Var is to be considered deprecated for all intense and purposes. Only to be used by pre-compilers and the alike.
And while vat never(or should never) be used in practice, it's still used in coding challenges because it's tricky behavior makes you really question your understanding under the hood of JavaScript.

Collapse
 
joshcheek profile image
Josh Cheek

Ahh, I was wrong! I guessed "C", for the same reason you were initially thinking.

Although I was a bit surprised it would let you declare the variable 2x. I guess because I've been using let which would error. eg

$ node -e 'var i = 0; var i = 1'
$ node -e 'let i = 0; let i = 1'

Anyway, the story was fun, too ๐Ÿ˜Š

Collapse
 
kenbellows profile image
Ken Bellows

This is awesome! Man that's a subtle bug, I don't know if I would have spotted it even if it was the cause

Collapse
 
chaznut profile image
James Nutter

Awesome article. How to see more of these!

Collapse
 
shiling profile image
Shi Ling

Thanks!

Collapse
 
tobiassn profile image
Tobias SN

let a = 3 could be pronounced like let a equal 3.

Collapse
 
davjvo profile image
Davmi Jose Valdez Ogando

Great exercise for teaching the importance of scope to new developers. Great one

Collapse
 
shiling profile image
Shi Ling

Yeah, as a senior dev, I forgot how confusing scoping in Javascript was in the beginning. It was a good exercise for me to find out the common gaps for JS beginners.

Collapse
 
metalmikester profile image
Michel Renaud

I'm afraid this begs for something from the index of the dBASE III Plus manual:

Endless Loop: See Loop, Endless

Loop, Endless: See Endless Loop

Collapse
 
offirmo profile image
Offirmo

Browser shouldn't crash for that...