2

I am getting results not expected with javascript variable redefined. I looked up that variable scope is not isolated so my question is, how can it be isolated so as to not cause conflictions with other code.

var test = "value";
var a = true;
if(a){
  console.log(test);
  var test = "another";
  console.log(test);
}
console.log(test);

Most would expect:

value
another
value

but actual:

value
another
another
InCode
  • 51
  • 6

3 Answers3

0

Variables declared with var are function scoped.

If you want block scope, ES6 introduces let:

let test = "value";
let a = true;
if(a) {
  console.log(test); // ReferenceError
  let test = "another";
  console.log(test); // "another"
}
console.log(test); // "value"

But let variables are still hoisted. So if you reference before iniltialization (temporal dead zone), you will get a ReferenceError instead of the value in the parent scope.

Community
  • 1
  • 1
Oriol
  • 225,583
  • 46
  • 371
  • 457
  • Nice thanks although from google android safari moble and safari desktop do not support right now. – InCode Mar 19 '16 at 03:33
  • 1
    @InCode Then you only have `var`, which is function scoped. Consider wrapping the block in an IIFE. – Oriol Mar 19 '16 at 03:35
0

I looked up that variable scope is not isolated

Where did you look that up? Every JS tutorial and help page makes it clear that variables declared with var have function scope.

I'm a little bit puzzled by the seeming fascination of beginning JS programmers with the questions of scoping and hoisting. The correct approach is:

  1. Declare your variables at the top of your function where they belong. Virtually all style guides, if you are following one, require this anyway. Virtually all linters, if you are using one, will, or can be configured to, enforce this.

  2. Spend the time you had planned to use for worrying about hoisting, variable scoping, and temporal dead zones instead to read a book, walk your dog, or actually do useful coding.

  3. Live a long and happy life.

If for some reason it is important for you to have variables scoped to a block (anything inside {}, such as the body of an if or for statement), then yes, use let. In the real world, I think you'll find such cases to be quite few, unless you are writing huge functions which contain dozens or hundreds of lines, which is not good practice to begin with. In this case, again follow best practice and declare all your variables at the top of the block.

If you don't have let available, and want to use another identically-named variable inside a block, then what about just using another variable? For instance, in the example you gave, inside the if block use test1 instead of test. This is actually how babel does it under the covers. It transpiles

function f() { var x; { let x; } }

into

function f() { var x; { var _x; } }
                            ^^

You will see it has renamed the inner x to _x.

Your question is essentially equivalent to wondering why it hurts when you stick pins in your eyes. Instead of worrying about different kinds of pins to stick in your eyes, or what the biological mechanism of the resulting pain is, why not stop sticking the pins in your eyes to start with?

  • Beginning JS programmers become fascinated with question of scoping and hoisting the minute that they are bitten by scoping and hoisting bugs. Which happens somewhere in the first 100 minutes for pretty much everybody. Curiosity about *how* this strange feature works is a **good thing**. Programmers that understand JS scoping avoid it out of understanding, whereas programmers that simply follow a style guide *without* understanding JS scoping avoid it out of superstition. I for one would prefer more programs written with understanding and fewer written with superstition. – meustrus Apr 08 '19 at 21:56
0

Vars in JavaScript are function-scoped only. So if you want to create a new scope, you have to put it in a function.

You can use a self-executing function to create an isolated scope:

var resultElem = document.getElementById('result');

var test = "value";
var a = true;
if(a){
  console.log(test);
  (function(){ // open scope of self-executing function
    var test = "another";
    console.log(test);
  })(); // close scope of self-executing function
}
console.log(test);

This is not terribly useful for your toy example, but it is used everywhere when the contents may inadvertently pollute globally-scoped variables.

Note that you will not get you expected behavior if you try to scope the entire if statement:

var resultElem = document.getElementById('result');

var test = "value";
var a = true;
if(a){(function(){
  console.log(test);
  var test = "another";
  console.log(test);
})()}
console.log(test);

The test = "value" variable is not in scope inside the self-executing function because the test = "another" variable is defined somewhere in that function and its declaration is hoisted to the top of its scope. The first console.log(test) in this example outputs undefined because test is in the "temporal dead zone" @Oriol mentioned.

meustrus
  • 5,075
  • 4
  • 35
  • 48