JavaScript hoisting variables from unexecuted branch - javascript

So i stumbled upon this behavior
var foo = "bar";
(function(){
if(false) {
var foo = "this truns the variable to undefined";
}
console.log(foo); // => undefined
})();
I understand the concept of hoisting. But shouldn't that only happen if the branch is executed?
var foo = "bar";
(function(){
if(false) {
foo = "this scenario is just fine";
}
console.log(foo); // => "bar"
})();

Due to variable hoisting and the fact that locally declared variables hide global variable of the same name, your first block of code is equivalent to this:
var foo = "bar";
(function(){
// define new local variable foo that hides the global one
var foo;
if(false) {
foo = "this truns the variable to undefined";
}
console.log(foo); // => undefined, local foo has not been assigned a value
})();
Because of hoisting, the variable is defined at the start of the function block, regardless of execution path within the function. Variables declared with var are scoped to the entire function and all definitions within the function are hoisted to the top of the function.
Variables defined with let (ES6 feature) are scoped to the block in which they are declared (still hoisted to the top of the block), but their definition is confined to the block in which they are declared, not the function in which they are declared. So, if you used let instead of var, you would get a different result.
Thus, in your first block of code, you are declaring a new variable foo at the start of your function and that new declaration will hide the global definition by the same name such that any reference to foo inside that function will refer to the local one.
The conditional if statement only affects the assignment to foo, not the declaration of foo. Thus, if the if statement does not execute, then foo is undefined since it was never assigned a value.
In your second code block, no new local foo variable is defined so all references to foo are simply referring to the global foo and since no new variable is declared, there is no hoisting. This second code block behaves identially whether the IIFE function is there or not - it's just a simple if statement that decides whether to change the value of the global foo or not.

Related

In javascript, are functions added to scope like variables are? [duplicate]

The var keyword in javascript causes a variable to be stored in the local scope. Without var variables belong to the global scope. What about functions? It's clear what happens when functions are declared like variables
var foo = function() {...}
but what scope does
function foo() {...}
belong to?
EDIT:
I realized I didn't ask quite the right question so as a follow up. In the outer most nesting is there a difference between the above two declarations and the following declaration?
foo = function() {...}
It belongs to the current scope, always. For example:
// global scope
// foo is a global function
function foo() {
// bar is local to foo
function bar() {
}
}
Regarding your second question, this:
foo = function() {...}
is an anonymous function expression assigned to a global variable (unless you're running is strict mode, then foo would be undefined). The difference between that and function foo() {} is that the latter is a function declaration (versus a variable declaration, which is assigned an anonymous function expression).
You might be interested in this excellent article about function declarations and function expressions: Named function expressions demystified.
Function declarations are always local to the current scope, like a variable declared with the var keyword.
However, the difference is that if they are declared (instead of assigned to a variable) their definition is hoisted, so they will be usable everywhere in the scope even if the declaration comes in the end of the code. See also var functionName = function() {} vs function functionName() {}.
Noteworthy distinction taking implicit globals into account:
var foo = function() {
// Variables
var myVar1 = 42; // Local variable
myVar2 = 69; // Implicit global (no 'var')
// Functional Expressions
var myFn1 = function() { ... } // Local
myFn2 = function() { ... } // Implicit global
function sayHi() {
// I am a function declaration. Always local.
}
}
Hopefully that clarifies a little. Implicit globals are defined if you forget a var before your assignment. Its a dangerous hazard that applies to variable declarations and functional expressions.
Your first example (var foo = function() {...}) is called an anonymous function. It is dynamically declared at runtime, and doesn't follow the same rules as a normal function, but follows the rules of variables.

global and local variable execution context in javascript

Trying to understand fundamentals of javascript I ran into a following code and expected value of variable named "foo" would be 7 and 5 however it came out as 7 and 7. Not sure why....
var foo = 5;
(function Test() {
foo = 7;
console.log("foo=" + foo);
})();
console.log("foo=" + foo);
foo=7
foo=7
Because when you do foo = 7; it makes a global variable and sets it to 7, even after the function is done it's still 7. You probably want it to be a local variable:
(function Test() {
var foo = 7;
console.log("foo=" + foo);
})();
To get 7 and 5, you need to put "var" before "foo = 7;" in your function in order to keep it from overwriting the the global foo you declared outside the function.
That is, you need to use var when declaring variables within functions to make them locally scoped to the function.
You are declaring a global variable and then referencing it inside your function. if you want a local variable, declare it inside your function using the var keyword.
//foo variable definition here
var foo = 5;
(function Test() {
//Referencing the already globally defined var.
//Use var foo = 7 in order to define a new local var.
foo = 7;
console.log("foo=" + foo);
})();
console.log("foo=" + foo);
Referencing a global variable inside an immediately called anonymous method doesn't mean it overrides the variable in the global scope!
Something like foo=7 will create a new variable only if there is no other foo that is accessible from the current scope. Which in this case exists!
Immediately called Anonymous function is not a completely isolated scope. As a function, it has a local scope, which is not available outside the block. But it still have access to the global scope.

Javascript: Global variable used before local variable is used (example from jQuery JavaScript tutorial)

In the jQuery tutorial on JavaScript (http://learn.jquery.com/javascript-101/scope/) there is the following example on scope:
var myFunction = function() {
var foo = "hello";
var myFn = function() {
console.log( foo );
};
foo = "world"; // line x
return myFn;
};
var f = myFunction();
f(); // "world"
Now I'm wondering why the output is world instead of hello. The variable foo is locally bound inside myFunction. Then in myFn, foo is accessed. I would say the evaluation should go down the scopes, which would be declaration inside myFn, then declaration inside myFunction and the last one should be the global variable.
So why is the global variable output even though the definition inside myFunction is less far away in the scope?
If I remove the line marked with line x, the local variable is output as expected.
foo is a local variable in the myFunction context. So when you call the myFn function you're accessing the actual (last in this case) value of the foo variable.
Note that there's an error in the question title. foo is not global. But local to myFunction
Functions are Objects in Javascript and Objects are passed by reference not value. Also, returning an Object is in essence passing the Object. Or, you could refer to it as returning by reference.
The myFn function being run in the global space retains the same scope it was defined in. This is what causes f() to adhere to changes made to the foo variable and to access the foo variable to begin with.
The layout of text is slightly misleading here http://learn.jquery.com/javascript-101/scope/. Text referring to code further down the page is followed by a colon. The text you are referring to is for the code above it, which is most likely what was causing confusion.

Definition vs initialization

What's the difference between declaration, definition and initialization? Example:
// Is this a declaration?
var foo;
// Did I defined object in here (but it is empty)?
var foo = {};
// Now that object is initialized with some value?
var foo = {first:"number_one"};
The first example is a declaration. You have declared a variable with the identifier foo. You haven't given it a value yet, so it will be undefined:
var foo;
console.log(foo); //undefined
The second example is a declaration and an assignment. You have assigned an empty object literal to the variable with the identifier foo. As noted in the comments, this is effectively short for:
var foo;
console.log(foo); //undefined
foo = {};
console.log(foo); //Object
The third example is another declaration and another assignment. You have assigned a different object literal to foo.
Edit (see comments)
The behaviour of your code is slightly different depending on whether you intended each example to run as an independent program, or as written (one program).
If you treat is as it's written:
Because variable declarations in JavaScript are hoisted to the top of the scope in which they appear, redeclaring variables has no effect. So the first line declares a variable foo.
The second line assigns an empty object literal to foo, and the third line assigns a different object literal to foo. Both of these assignments apply to the same foo.
What effectively happens is this:
var foo;
foo = {}; //No `var` keyword
foo = {first:"number_one"}; //No `var` keyword
If you treat each line as a separate program:
The first program declares a variable named foo. It's value is undefined.
The second program declares a variable named foo, and then assigns an empty object literal to it.
The third program declares a variable named foo and then assigns an object literal with one property to it.
You got it right.
var foo; // Is this a declaration ?
Yes, you declared that there's a variable named foo, but didn't define foo (so foo is undefined).
var foo = {} // Did I defined object in here (but it is empty) ?
Yes, now you "defined" foo... it has a value, it is no longer undefined. var foo = 5 also counts as "defining" it.
var foo = {first:"number_one"} // Now that object is initialized with some value ?
You could say that it's "initialized," but that's really just semantics. "Declared" and "defined" are a bit more meaningful.
Run the code below:
var foo;
console.dir(foo);
var foo = {};
console.dir(foo);
var foo = {first:"number_one"};
console.dir(foo);
When you declare a variable with var foo; you actually ensure that it will belong to the scope where you've defined it. What you call definition and initialization is in fact a value assignment.
Consider following piece of code as an example:
(function () {
// definition
var foo;
function assignFoo(x) {
// assignment
foo = x;
}
assignFoo(5);
console.log(foo);
})();
To say, you're not always supposed to assign value within the scope of definition. But it is the most common use case which is usually accomplished with var foo = 5.
Thats it.

No need to define a variable twice

If a variable could be defined in a function, even if no value is assigned, it becomes a local variable
so, is testB() better programming?
var test = 'SNAP!'
function testA(boolean) {
if (boolean) var test = 'OK';
else var test = null;
alert(test);
}
function testB(boolean) {
if (boolean) var test = 'OK';
alert(test);
}
testA(true); // 'OK'
testB(true); // 'OK'
testA(false); // null
testB(false); // undefined, no error
In my specific case test's global value ('SNAP!') is neither expected nor required.
You can't declare variables conditionally.
Why?
The variable instantiation process occurs before the actual code execution, at the time the function is executed, those variables will be already bound to the local scope, for example:
function foo () {
if (false) {
var test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
When the function is about to be executed, identifiers of formal parameters, identifiers from variable declarations, and identifiers from function declarations within the function's body are bound to the local variable environment.
Variables are initialized with undefined.
Also, identifiers in the local scope shadow the others with the same name, higher in the scope chain, for example:
var test = 'global';
function bar () {
alert(test); // undefined, not "global", the local variable already declared
var test = 'xxx';
}
bar();
If the test variable were not declared anywhere, a ReferenceError will be thrown:
function foo () {
return test;
}
try {
foo(); // ReferenceError!!
} catch (e) {
alert(e);
}
That's one of the reasons about why for example, JSLint recommends only one var statement at the top of functions, because for example, the first snippet, will actually resemble this when executed:
function foo () {
var test; // var statement was "hoisted"
if (false) {
test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
Another reason is because blocks don't introduce a new lexical scope, only functions do it, so having a var statement within a look might make you think that the life of the variable is constrained to that block only, but that's not the case.
Nested function declarations will have a similar behavior of hoisting, they will be declared before the code execution, but they are initialized in that moment also:
function foo () {
return typeof bar;
// unreachable code:
function bar() {
//..
}
}
foo(); // "function"
If the variable does not need to be manipulated by any other functions, keep the variable inside a function with var foo;.
Otherwise, if it does need to be accessed and read in multiple scopes, keep it outside. But remember that when you keep it outside, it becomes global. That is, unless you wrap everything in a self executing function, which is the best way:
(function() {
var president='bush';
function blah() {
president='reagan';
}
function meh() {
president= 'carter';
}
document.getElementById('reagan').onclick=blah;
document.getElementById('carter').onclick=meh;
})();
alert( president ) // undefined
The above is perfect for a variable accessed by functions defined inside of that scope. Since there are 2 elements i click to set the president, it makes sense to define it outside both functions because they set the same variable.
So, If you are not dealing with multiple functions changing the exact same variable, keep them local to the function.
Is testB better programming? No, because it gives an unexpected result of "undefined" (at least, I was surprised by that) and it is hard to read.
Generally, variables should be limited to the scope that requires them, so if the "test" variable is not needed outside the function it should be declared local. To avoid confusion, declare your variable before using it:
function testC(boolean) {
var test;
if (boolean) {
test = "OK";
}
else {
test = null;
}
alert(test);
}
Unless you genuinely want to change the global scope version of "test", in which case don't use the var keyword inside the function.
If you ever find yourself using the same name for a local variable and a global variable you might consider renaming one of them.

Categories