Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

var & lat in inner functions

Hi, everyone! A few minutes ago, I browsed in the MDN website and I noticed something strange. Here is The code:

var list = document.getElementById('list');

for (let i = 1; i <= 5; i++) {
  let item = document.createElement('li');
  item.appendChild(document.createTextNode('Item ' + i));

  item.onclick = function(ev) {
    console.log('Item ' + i + ' is clicked.');
  };
  list.appendChild(item);
}

// to achieve the same effect with 'var'
// you have to create a different context
// using a closure to preserve the value
for (var i = 1; i <= 5; i++) {
  var item = document.createElement('li');
  item.appendChild(document.createTextNode('Item ' + i));

  (function(i){
    item.onclick = function(ev) {
      console.log('Item ' + i + ' is clicked.');
    };
  })(i);
  list.appendChild(item);
}

The example above works as intended because the five instances of the (anonymous) inner function refer to five different instances of the variable i. Note that it does not work as intended if you replace let with var, since all of the inner functions would then return the same final value of i: 6. Also, we can keep the scope around the loop cleaner by moving the code that creates the new elements into the scope of each loop.

I totally don't understand it, Can someone explain it to me?

Thanks, Yuval.

4 Answers

Ari Misha
Ari Misha
19,323 Points

Hiya Yuval! The "let" keyword allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Now that we understand the difference between "let" ad "var", lets compare the two examples in your code. The first block code, has a "list" variable defined with new "let" keyword. Followed by a "for" loop, in which we are a creating an element "li", and appending "list" variable plus "i" alias to "item" variable. Followed by a click event on "item" variable.

Thats exactly what second block of code does too. But here comes the important part of the answer. The "let" keyword allows you to define the scope of the variable. Especially when it comes to "for" loop, "let" keyword works exactly as a developer wants it to. Whereas had it been "var" keyword instead of "let", the loop would output only one entry with local variable "i" assigned to 5, right? Thats why JS community introduced "let" keyword coz it is limited to the scope of the block.

Lets just say even if you want to execute the "for" loop with "var" keyword. You're gonna have to immediately invoke the function expression inside your loop to prevent the bug that we talked about in above paragraph. IIFE(immediately invoked function expression) will get invoked as soon as JS interpreter hits the function expression , rather than waiting for "for" loop to exit. Hence, IIFE will get executed everytime as long as "i <=5" is "true".

I hope this helped. (:

Ari Misha
Ari Misha
19,323 Points

Hiya again! The above code doesnt work coz JS interpreter is confused about reassigning local variable "i" in your code. Remove "var i = i " and try to run it with IIFE , that we talked about before. And It'll pass with flying colors. (:

Wow! Great explanation! Thank you very much!
But when I wrote something like this;

for (var i = 1; i <= 5; i++) {
    setTimeout(() => {
        var i = i; // I defeind the scope here :) 
        console.log(i);
    }, 0);
}
It should work, but it doesn't...

I understand that if I will change it to (below) will work, but I think that I wrote the same functionally with the code above; "The let keyword allows you to define the scope of the variable. "

for (let i = 1; i <= 5; i++) {
    setTimeout(() => {
        console.log(i);
    }, 0);
}

Go it!

for (var i = 1; i <= 5; i++) {
    (function (i) {
        setTimeout(() => {
            console.log(i);
        }, 0)
    })(i);
}