Javascript for loop index variables become part of global scope? - javascript

Perhaps I'm not aware of how for loop index variables get scoped, but I was very surprised when one of my loops didn't complete, seemingly because a function called from within a loop contained an i for its for loop index as well.
Here's a little script I put together to demonstrate this behavior:
var loopOne = function(test) {
for(i = 0; i < test.length; i++)
console.log(getMask(test));
};
var getMask = function(pass) {
var s = "";
for (i = 0; i < pass.length; i++) {
s = s + "*";
}
return s;
};
loopOne('hello');
If I run this in Chrome and look at the console log, I should see ***** five times. However, I only see it once. Upon further inspection, if I type i in the Chrome javascript console, it will output 6 ( = 'hello'.length + 1). This makes me think that i has become a part of the global scope and is not limited to the scope of the for loop for which it was needed.
Is this correct? If so, what's a better practice for defining the index variable of a for loop in javascript?

In Javascript, variables are scoped with the var keyword. When declaring variables with var, the variable is scoped to the current function. When assigning to a variable without using the var keyword, it is assumed you're talking about an already defined variable in the same or a higher scope. If none is found, the variable is created in the highest scope.
Bottom line: declare all your variables using var.

You should declare your loop index variable with let:
for (let i = 0; i < test.length; i++) ...
That will declare and set the proper scope for the variable.

When you declare your variables with var, you scope them to the current execution context.
When you don't, they become properties of the global object (window in a browser).

Related

Must we include Let keyword in javaScript For Loops?

I am learning about For Looops and I find that the following two pieces of code work just the same.
Code 1:
for (let i=0 ; i<bobsFollowers.length; i++){
for ( let j=0; j<tinasFollowers.length; j++){
if (bobsFollowers[i] === tinasFollowers[j]){
mutualFollowers.push(tinasFollowers[i]);
}
}
}
console.log(mutualFollowers);
Code 2
for (i=0 ; i<bobsFollowers.length; i++){
for ( j=0; j<tinasFollowers.length; j++){
if (bobsFollowers[i] === tinasFollowers[j]){
mutualFollowers.push(tinasFollowers[i]);
}
}
}
console.log(mutualFollowers);
You can globally access the variable if you do not include let keyword
for (let i = 0; i < 3; i++) {
console.log(i);
}
for (j = 0; j < 3; j++) {
console.log(j);
}
console.log('j ', j); // accesible
console.log('i ', i); // inaccesible
Yes, if you do not specifically use the let keyword in a for, for...of or for...in loops etc, they will work the same as when you use the let keyword.
However, when you don't explicitly declare using let keyword, the variable will be declared as a var. let and const are block level variables, but you also need to understand that 'functions are not the only blocks, any { } is basically a block'. i.e. Once declared a 'var' will become a function scoped variable and in case it is not inside a function, and declared outside, then it becomes globally available, which is not such a good thing considering you might have multiple for loops using a variable named 'i' and if you don't use let, it will just keep incrementing the old 'i'. Please see the below two examples to understand what I mean by the above sentence:
const test = function(){
let let1 = 1;
var var1 = 2;
{
let let1 = 9; //This is a block scoped variable limited to this { } block
var var1 = 3; //re-initialization still overrides the original var 1 variable's value because once declared, the var variable is function scoped
console.log(let1); //9 - Block Scoped
console.log(var1); //3 - Function Scoped
}
console.log(let1); //1 - Function Scoped
console.log(var1); //3 - Still Function Scoped, always function scoped
}
test();
What I meant by globally incrementing value of 'i':
for(i=0; i< 2; i++){
console.log('first loop', i)
}
for(i;i< 5; i++){//I am not initializing i to 0, but I'm just trying to show that it is a global variable now.
console.log('second loop', i)
}
for(i; i< 7; i++){ //I am not initializing i to 0, but I'm just trying to show that it is a global variable now.
console.log('third loop', i)
}
Secondly, in ES5, Strict Mode was introduced, which basically is a way you can opt out of writing 'sloppy mode' JS code, and you can use it by specifying 'use strict'; globally or at a function level. And strict mode is used in most companies and developers in their professional code bases.
The point I want to convey with this is that 'Strict Mode' does not allow you to use for loops without declaring the variable, you have to explicitely specify the 'var' or 'let' keywords. Therefore it is always a good practice to declare your variables. You can read more about strict mode here: Strict Mode
Lastly, in the current post ES6 times, using the var keyword to declare variable is not considered as a good practice due to something called as Hoisting, which basically means that before the a var x; is declared, if you try to use it, you still can, however it's value will be 'undefined'. But in case of let you cannot use a variable before you declare it. You can read more about it Here : Hoisting
Also, in case of let if you try to access a variable before it's initialized, it throws you a Reference Error. This is because of a concept called as a 'Temporal Dead Zone'. You can read more about it here: TDZ
'let' is all about lexical scope, by default variables and objects in javascript have a global scope. As other have pointed out, using var will make the scope of variable global so anything can access and modify it.
Another use case for let would be in closures.
This example is from CS50's react native lecture 0 & 1.
Here makeFunctionArray returns an array of functions which print the value of i.
function makeFunctionArray() {
const array = [];
for(var i=0; i<5; i++) {
array.push(function () { console.log(i) });
}
return array;
}
const functionArray = makeFunctionArray();
functionArray[0]();
Now, what do you expect functionArray[0]() to print?
0, right? because we're invoking the function at index zero and it should console log 0.
But it doesn't print 0, instead it prints 5.
This is because 'i' has a global scope and will have a value of 5 when loop terminates.
the function(closure) we're returning from the makeArray function will still have access to 'i' and the value 5 gets wrapped up in it while being returned. so every functionArray[index]() will print 5.
This can be avoided with 'let' if 'i' is a let, its scope will only be the 'for' loop.
At the end of this lecture, this scenario is introduced and answered in next lecture
Yes, you have to. Or the old var
As other responses mention you will populate this variable in a broader scope.
Imagine this situation, You need to create a function that print the first 10 numbers:
function printNumbers(){
for (i=0; i<10; i++) {
console.log(i)
}
}
That's cool. Now you need to invoke this function 12 times, easy right?
function printNumbers(){
for (i=0; i<10; i++) {
console.log(i)
}
}
for (i=0; i<12; i++) {
printNumbers()
}
Well, if you execute this in your google chrome console (don't). You will fry your browsers. Since i will never reach 12 and you will be in an infinite loop.
You can test this safely by changing 12 by 5, you will see that the function only runs 1 time and not 5.
So it is not for your code (only), it is for who is going to use your code in the future. You are leaving a potential big fail in your code. Imagine it is in a library.
You have a good explanation already in StackOverflow about scoping and let here: What's the difference between using "let" and "var"?
Also about scope in general here: What is the scope of variables in JavaScript?
The scope of let is restricted to that block alone. but if you do not specify let keyword it is scoped to the global environment(the global scope of the browser is window object)
{
let a = 1;
b = 2;
}
console.log(a) //error
console.log(b) //2

In a loop, is it better to redefine a global variable or to redeclare and redefine a local variable over and over, or is there no difference?

For example:
is
for(var i = 1; i<100; i++){
var inc = 1/i*PI;
//and so forth
}
in any way better or worse than
var inc = 1/1*PI;
for(var i = 1; i<100; i++){
inc = 1/i*PI;
}
Of course the first is easier to type, but maybe it takes away speed/performance (even if a little bit) from the program when constantly re-declaring the same variable versus reassigning values to a global variable. Thank you.
Because of var hoisting, there is absolutely no difference between the two. And since it makes no difference, according to the docs:
For that reason, it is recommended to always declare variables at the top of their scope (the top of global code and the top of function code) so it's clear which variables are function scoped (local) and which are resolved on the scope chain.
Now if you were using let instead of var, the story would be different. I don't think there would be any performance difference at all, but there would certainly then be a semantic difference. The documentation for let goes into detail about those differences.
The second approach is correct. You should only declare variables once.
I would code your example like so:
var i,inc = 1/1*PI;
for(i = 1; i<100; i++){
inc = 1/i*PI;
}
This puts all the variable declarations in one place which makes it easier to read the code.
If you would like to use block level scope, use the let statement, like so:
var i;
for(i = 1; i<100; i++){
let inc = 1/i*PI;
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Mutating key inside JavaScript's for..in loop [duplicate]

What's the correct way to write a for-in loop in JavaScript? The browser doesn't issue a complaint about either of the two approaches I show here. First, there is this approach where the iteration variable x is explicitly declared:
for (var x in set) {
...
}
And alternatively this approach which reads more naturally but doesn't seem correct to me:
for (x in set) {
...
}
Use var, it reduces the scope of the variable otherwise the variable looks up to the nearest closure searching for a var statement. If it cannot find a var then it is global (if you are in a strict mode, using strict, global variables throw an error). This can lead to problems like the following.
function f (){
for (i=0; i<5; i++);
}
var i = 2;
f ();
alert (i); //i == 5. i should be 2
If you write var i in the for loop the alert shows 2.
JavaScript Scoping and Hoisting
The first version:
for (var x in set) {
...
}
declares a local variable called x. The second version:
for (x in set) {
...
}
does not.
If x is already a local variable (i.e. you have a var x; or var x = ...; somewhere earlier in your current scope (i.e. the current function)) then they will be equivalent. If x is not already a local variable, then using the second will implicitly declare a global variable x. Consider this code:
var obj1 = {hey: 10, there: 15};
var obj2 = {heli: 99, copter: 10};
function loop1() {
for (x in obj1) alert(x);
}
function loop2() {
for (x in obj2) {
loop1();
alert(x);
}
}
loop2();
you might expect this to alert hey, there, heli, hey, there, copter, but since the x is one and the same it will alert hey, there, there, hey, there, there. You don't want that! Use var x in your for loops.
To top it all off: if the for loop is in the global scope (i.e. not in a function), then the local scope (the scope x is declared in if you use var x) is the same as the global scope (the scope x is implicitly declared in if you use x without a var), so the two versions will be identical.
You really should declare local variables with var, always.
You also should not use "for ... in" loops unless you're absolutely sure that that's what you want to do. For iterating through real arrays (which is pretty common), you should always use a loop with a numeric index:
for (var i = 0; i < array.length; ++i) {
var element = array[i];
// ...
}
Iterating through a plain array with "for ... in" can have unexpected consequences, because your loop may pick up attributes of the array besides the numerically indexed ones.
edit — here in 2015 it's also fine to use .forEach() to iterate through an array:
array.forEach(function(arrayElement, index, array) {
// first parameter is an element of the array
// second parameter is the index of the element in the array
// third parameter is the array itself
...
});
The .forEach() method is present on the Array prototype from IE9 forward.
Actually, if you dislike declaration within for heading, you can do:
var x;
for (x in set) {
...
}
As mentioned in other answers to this question, not using var at all produces unnecessary side-effects like assigning a global property.
Use the one where you declare the loop variable with var. Implicitly declared variables have a different scope that's probably not what you intended.
for(var i = 0; ...)
is a commonly seen pattern but it's different from
for(int i; ...)
in C++ in that that the variable isn't scoped to the for block. In fact, the var gets hoisted to the top of the enclosing scope (function) so a local i will be effectively available both before the for loop (after the beginning of the current scope/function) and after it.
In other words, doing:
(function(){ //beginning of your current scope;
//...
for(var i in obj) { ... };
})();
is the same as:
(function(){ //beginning of your current scope;
var i;
//...
for(i in obj) { ... };
})();
ES6 has the let keyword (instead of var) to limit the scope to the for block.
Of course, you SHOULD be using local variables (ones declared with either var or let or const (in ES6)) rather than implicit globals.
for(i=0; ...) or for(i in ...) will fail if you use "use strict"; (as you should) and i isn't declared.
Using var is the cleanest way, but both work as described here: https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
Basically, by using var you ensure that you create a new variable. Otherwise you might accidentally use a previously defined variable.
I think var is good for performance reasons.
Javascript won't look through the whole global scope to see if x already exists somewhere else.
From a general point of view, first version will be for an index that must live within loop's scope, while the other one would be any variable in the scope where loop's constructor got invoked.
If you're going to use loop's index inside for loop and this won't be required by others in next lines, better declare the variable with "var" so you'll be sure "x" is for loop's index initialized with 0, while the other one, if other "x" variable is available in this context, this will get overwritten by loop's index - that's you'll have some logical errors -.
I always use the block scoped let introduced in ES2015.
for (let x in set) {
...
}
Additional reading and examples

Using var when declaring i in a for loop [duplicate]

This question already has answers here:
"var" or no "var" in JavaScript's "for-in" loop?
(10 answers)
Closed 7 years ago.
I've seen Javascript code that has used two different ways of defining a for loop.
for (var i=0;i < x.length; i++)
But it's also been
for (i=0; i < x.length; i++)
The same thing has happened with for-in loops
for (var i in x)
and
for (i in x)
Is there any difference between declaring i as a var and just saying i? Are there advantages of doing one over the other? Is one these right way to do this? From what I can tell, they both act the same, but there has to be some difference.
Note: I'm not asking about the difference between for-in and for (i=0)
Without a var declaration somewhere in a function, references to i will be to the i property of the global object. This risks all sorts of unpredictable behavior if code in the body of the for loop invokes code (say, inside a called method) that also modifies the global i.
Note that declaring var i in the for loop initialization
for (var i = ...)
is equivalent to declaring var i; before the for loop:
var i;
for (i = ...)
In particular, the declaration of i will be hoisted to the top of the enclosing scope.
EDIT: If you enable strict mode, then you must declare your loop variables (all variables, actually). Referencing a variable that has not been declared with a var statement will result in a ReferenceError being thrown (rather than resulting in a global variable coming into existence).
JavaScript is functionally scoped. At least for now, the language does not have block level variables.
When you write for (var i=0; ... ); it is the same as
var i;
for (i=0; ... );
In the absence of "use strict"; variable declaration will be hoisted.
for (i=0; ... ); alone implies that i belongs to the global (top most) object (in browsers it's window)
Intro:
The for statement creates a loop that consists of three optional
expressions
Source: Mozilla JavaScript Docs
Background:
You are referring to initialization, which is an expression or variable declaration. It's almost always used to initialize a counter variable that allows us to iterate through a collection as you've shown.
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.
In JavaScript, variables can hold different data types, and in the case of a counter variable, JavaScript treats the variable as a number.
Answer:
The reason one can optionally declare a new variable (or not at all, as you've highlighted) is due to the nature of the JavaScript programming language. You've hit upon an important aspect of the language that deals with variables and scope.
"To var or not to var"
Please see this other post about using var or not using it at all to understand more...
What is the function of the var keyword and when to use it (or omit it)?
If you say start using "i" without saying "var" first, you should have declared the variable before your for loop.
Like:
var i;
for (i = 0; i < x; i++) {
doSomething();
}
==OR==
for(var i = 0; i < x; i++){
doSomething();
}

Reusing "i" for JavaScript for loops

I recently started using PHP Storm (love it) and noticed that it flags declarations of "var i" in for loops as duplicate in JavaScript. I learned that apparently the scope of that variable exists outside of the loop.
for (var i = 0; i < 10; i++) {
// Do anything
}
console.log(i); // i == 10
When I do my next for loop, should I declare var i again? Or, should I just say i = 0? I know I can do either, but one seems like bad style, the other like bad implementation.
On one hand, you shouldn't re-declare a variable that's in scope, but if I, for instance, delete the first for loop that declares "i", then everything else will break.
JavaScript has only function level scope, no block level scope. So, if a variable is declared anywhere within a function, it will be available for the entire function. So, you don't have to declare it again.
The best practice is to declare all the variables used in the function at the beginning of the function.
For example,
function myFunction() {
console.log(i);
for (var i = 0; i < 10; i++);
console.log(i);
}
myFunction();
Would print,
undefined
10
i is declared in the function, but till the for loop is executed, i is not assigned any value. So, it will have the default value undefined in it.
you need not declare it again. you can simply reassign the value of i for the next loop like
for (i = 0; i < 5; i++)
{
// Do anything
}

Categories

Resources