Can JavaScript get confused between local variables? - javascript

If I have a pair of functions that both set local variables, for example, the variable i in common for loops, and one happens to be called while the other is running, is there any danger of namespace confusion?

Keep in mind that JavaScript does not have block scope, but only function scope.
In addition, if you have nested loops, there will only be one i variable in the following example:
function myFunction() {
for (var i = 0; i < 10; i++) {
for (var i = 0; i < 10; i++) {
// code here will run 10 times instead of 100 times
}
}
// variable i is still accessible from here
}
Douglas Crockford recommends that the var statements should be the first statements in the function body in Code Conventions for the JavaScript Programming Language:
JavaScript does not have block scope, so defining variables in blocks can confuse programmers who are experienced with other C family languages. Define all variables at the top of the function.
I think he has a point, as you can see in the following example, which will not confuse readers into thinking that the variables i and j are held in the scope of the for loop blocks:
function myFunction() {
var i, j; // the scope of the variables is now very clear
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
// code here will run 100 times
}
}
}

As long as you're using var, like this:
for(var i = 0; i < something; i++)
Then it's local and you're fine, if you don't use var, you have a global variable on your hands, and potential issues. Also if a for loop is nested in another, you should use a different variable name for each loop.

It will be a problem if are you referring to nested loops. Every time you enter the second for loop, the value of i (previously set in the outer for loop) will be reset.

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

Declaring variables inside or outside in a for-in loop

Having this two options:
Option A:
var index;
for (index in myObject) {
// Do something
}
Option B:
for (var index in myObject) {
// Do something
}
I don't know if in the option B the variable index it's being redeclared every time of the loop or just once.
Those two snippets of code do exactly the same thing (and that's the case in most language such as C, C++ and C# amongst others). If the variable was redeclared at every iteration, then following your logic, it would also be re-initialized, and would constantly loop over the same object. Your loop would be infinite.
On a side-note, in JavaScript, all variable declarations get pushed to the function scope; this means that you can declare variables anywhere within a function, even within nested loops, and they will only be declared once.
Link to the var documentation
Relevant SO question
Other relevant SO answer
Edit courtesy of #torazaburo:
If you want to declare a variable with a local scope (as in, a variable that will only be defined in the current block such as a for, while or if, you can use the let statement:
let var1 = 123;
It also allows you to override variables with the same name but declared in a higher scope, such as in this example from the docs:
function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
See the full documentation (and examples) here.
The preferred approach in 2016 is to declare the variable in the loop head, using let:
for (let i = 0; i < max; i++) { }
^^^
There is probably minimal performance difference between this and other approaches, but there are major advantages in terms of the robustness and clarity of your code.
First, with let, i is local to the for construct, so it can't "leak out", or modify some other i in an outer scope.
Second, and perhaps more importantly, a new version of i is created for each iteration of the loop. In technical terms, "you get a fresh binding for each iteration if you let-declare a variable" (see this excellent article). This solves the age-old problem of closures created inside the for-loop using the final value of i. We can now just write
for (let i = 0; i < 10; i++) {
setTimeout(() => alert(i), i * 1000);
}
instead of having to do some clumsy work-around such as
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(() => alert(i), i * 1000);
}(i));
}
which has been the topic of countless questions here on SO, and which many of you have wasted far too many brain cycles learning.

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
}

how often should a javascript variable be defined?

I've always defined the counter variable using var in my JavaScript for loops. I've recently realized that I am defining the same variable twice when I have multiple loops in a given script. Is there any problem with doing the following, or should I only define i once?
for (var i = 0; i < var1.length; i++){
//do something with var1[i]
}
for (var i = 0; i < var2.length; i++){
//do something with var2[i]
}
Your code will work (although could have unforeseen consequences) because of hoisting. Hoisting means that all declarations in a function are "hoisted" to the top during interpretation (which is fine because JavaScript has function scope anyway). Your code winds up being interpreted as:
function() {
var i;
for(i = 0; i < var1.length; i++) { }
for(i = 0; i < var1.length; i++) { }
}
So you can see that even though you think you declared i multiple times, both declarations are hoisted to the top and re-use the same variable.
As long as the two are used independently, you shouldn't technically have any issues. Unfortunately it makes your code less readable.
While declaring & initializing a variable in each for loop may give you a small overhead (with small I mean really small), I don't think that you should switch to declaring only once.
Declaring & initializing the counter variable in every for-loop separately is much more easily to read / handle and far more error-proof (what if you forget to reset the counter variable after a for loop?).
A variable should only be defined once in its scope, unless available in the lexical scope. For your example, you should be doing:
var i;
for (i = 0; i < var1.length; i++){
//...
}
for (i = 0; i < var1.length; i++){
// ...
}
This doesn't prevent changes to that variable however.
If I do:
var i = 0;
function A () {
i = 1;
}
A();
This will change i to 1. You're not redefining it, but passing it about and changing its value. This of course is a very simple example.
Variables are only contained within the statement. The only time you would have to worry about multiple variables are in either nested statements or re-declaring predeclared variables.

Javascript for loop index variables become part of global scope?

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).

Categories

Resources