JavaScript loop variable scope

Just a quick question about the scoping of JavaScript variables.

Why does the alert() function print the value of i instead of returning undefined?

$(document).ready(function () {
    for(var i = 0; i < 10; i += 1){
    }

     alert("What is 'i'? " + i);
});

I'm fairly new to JS, and in nearly all other languages I've dabbled, a declaration in the scope of the for loop would contain the value to that said loop, but not in this case, why?

i.e. What is 'i'? 10' is printed.

Answers:

Answer

See the MDN for the "initialization parameters" of a for-loop:

An expression (including assignment expressions) or variable declaration. Typically used to initialize a counter variable. This expression may optionally declare new variables with the var keyword. These variables are not local to the loop, i.e. they are in the same scope the for loop is in. The result of this expression is discarded.

Answer

JavaScript didn't have block scope until const and let were introduced, just var which is function scoped. Since the initialization of i is within one function, that variable is accessible anywhere else in that same function.

From MDN:

Important: JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. In other words, block statements do not introduce a scope. Although "standalone" blocks are valid syntax, you do not want to use standalone blocks in JavaScript, because they don't do what you think they do, if you think they do anything like such blocks in C or Java.

Answer

The javascript folks are trying to fix this!

EcmaScript6 (aka EcmaScript 2015) is the latest version of javascript that was passed last summer and browsers are just starting to support its features.

One of those features is block-scope local variables with the "let" expression. As of right now (April 2016), most of the current versions of the major browsers support this except Safari. Few mobile browsers support this.

You can read more about it here (in particular, see the section "let-scoped variables in for loops"): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

You can check current browser support here (look for the row Bindings -> let): https://kangax.github.io/compat-table/es6/

Answer

Unlike other languages (for example: Java, C++, C), JavaScript doesn't support block scope. Once you declare a variable in a loop or in a function it's scope is within the function body if you do

for(i=0; i<arr.length; i++) {
    var j=0;
    // ...
}

here your i becomes a global variable and j become local to the function or script in which the loop is.

Answer
for(i=0; i<arr.length; i++) {
    var j=0;
    // ...
}

it is not correct to state that the above creates a global variable i. I believe you should always use var to declare variables (unless you are intentionally wanting a 'property' rather than a 'variable' -which is pretty unlikely in 99.99% of JS coding scenarios ...)

Omitting var when assigning an initial value to i isn't creating a local or even a global variable, it is creating a property i for the global object (which may seem/behave mostly like a global variable - but they have some subtle differences).

better would be:

var i;
for(i=0; i<arr.length; i++) {
    var j=0;
    // ...
}

now the loop is using a global variable i (or function local variable i, if this code appears in a function)

see more about this at what is function of the var keyword and variables vs. properties in Javascript

-- note, what is a little confusing is that you can re-declare a variable, for example in a second loop

for(var i=0; i<9; i++){
    document.write('i = ' + i + '<br>');
}


for(var i=0; i<9; i++){
    document.write('i = ' + i + '<br>');
}

this seems to be valid (no errors when I test). It seems that you CAN re-declare variables in JavaScript - but it probably isn't every a good idea, unless a special case - see this related question mentioning how [Google Analytics makes use of the 'safe' redeclaration of a variable] (Redeclaring a javascript variable)

there is some discussion about re-declaring variables in JS (and also loop variables like i) in this related SO question: declare variables inside or outside the loop

There is event a JavaScript pattern for single declaration of variables

Answer

In the click handler you need to store the selected color in a separate variable, e.g. selectedColor.

Furthermore, it is not allowed to use the same id value more than once. I did not fix this because it wasn't needed for the snippet to work, but it is something you better take care of in order to prevent the code from 'mysteriously' not working in some browsers.

Fixed code:

function myFunction() {
  const imageNumber = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
  const imageColor = ['pearl-white', 'ruby-black-pearl', 'alloy-silver-metallic', 'mercury-gray-metallic', 'red-diamond', 'quartz-brown-metallic'];
  const img = document.querySelector('#image');
  const colorButton = document.querySelectorAll('#color');
  const next = document.querySelector('#next');
  const prev = document.querySelector('#prev');
  let index = 0;
  let selectedColor = 0;

  img.setAttribute('src', 'https://mcarsstatic.cachefly.net/img/vehicles/phev/2020/trims/GT-S-AWC/360/' + imageColor[selectedColor] + '/2020-phev-360-' + imageNumber[index] + '-d.png');

  for (let i = 0; i < colorButton.length; i++) {
    colorButton[i].addEventListener('click', function() {
      selectedColor = i;
      img.setAttribute('src', 'https://mcarsstatic.cachefly.net/img/vehicles/phev/2020/trims/GT-S-AWC/360/' + imageColor[selectedColor] + '/2020-phev-360-' + imageNumber[index] + '-d.png');
    });
  }

  next.addEventListener('click', function() {
    index++;
    if (index >= imageNumber.length) {
      index = 0;
    }
    img.setAttribute('src', 'https://mcarsstatic.cachefly.net/img/vehicles/phev/2020/trims/GT-S-AWC/360/' + imageColor[selectedColor] + '/2020-phev-360-' + imageNumber[index] + '-d.png');
  });

  prev.addEventListener('click', function() {
    index--;
    if (index <= 0) {
      index = imageNumber.length - 1;
    }
    img.setAttribute('src', 'https://mcarsstatic.cachefly.net/img/vehicles/phev/2020/trims/GT-S-AWC/360/' + imageColor[selectedColor] + '/2020-phev-360-' + imageNumber[index] + '-d.png');
  });
}

myFunction();
html,
body {
  width: 100%;
  position: relative;
  font-size: 100%;
  font-weight: normal;
  font-family: 'Noto Sans', sans-serif;
}

* {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  border: 1px solid black;
}

div.img-item {
  position: relative;
}

div.img-item>img {
  position: absolute;
  top: 0;
  left: 0;
  background-color: white;
}

div.arrow-item {
  height: 462px;
}

span#next {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 2rem;
  font-weight: bold;
  cursor: pointer;
}

span#prev {
  position: absolute;
  top: 50%;
  right: 50%;
  transform: translate(-50%, -50%);
  font-size: 2rem;
  font-weight: bold;
  cursor: pointer;
}

div.colors {
  display: flex;
  justify-content: center;
  padding: 15px;
  cursor: pointer;
}

#color {
  padding: 15px;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
<section class="section">
  <div class="container">
    <div class="row">
      <div class="col-md-1 arrow-item">
        <span id="next"><</span>
      </div>
      <div class="col-md-10 img-item">
        <img id="image">
      </div>
      <div class="col-md-1 arrow-item">
        <span id="prev">></span>
      </div>
    </div>
    <div class="row">
      <div class="col-md-4"></div>
      <div class="col-md-4">
        <div class="colors">
          <div id="color" style="background-color: white;"></div>
          <div id="color" style="background-color: black;"></div>
          <div id="color" style="background-color: silver;"></div>
          <div id="color" style="background-color: gray;"></div>
          <div id="color" style="background-color: red;"></div>
          <div id="color" style="background-color: brown;"></div>
        </div>
      </div>
      <div class="col-md-4"></div>
    </div>
  </div>

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.