In the following code:
var greeting = "hi";
function changeGreeting() {
if (greeting == "hi") {
var greeting = "hello";
}
alert(greeting);
}
changeGreeting();
...greeting is undefined. However if I remove the var and change changeGreeting() to this:
function changeGreeting() {
if (greeting == "hi") {
greeting = "hello";
}
alert(greeting);
}
...I get "hello" as expected.
I would never redeclare a variable like this in my code, but why does this happen?
JavaScript variables have function scope. Thus, the very presence of var greeting inside the function will declare a local greeting variable, which will be undefined at the time of its mention in if condition: the global variable will not be visible inside the function, being overshadowed by the local one. Therefore, the if does not happen, the assignment to hello doesn't happen, the variable is still undefined.
In the second example, you're using the global variable throughout, it is not overshadowed by a local variable (because, no var greeting inside the function), and things work as you expect.
It's very simple: JS hoist the Variable declarations to the top of the current scope, but any operations (including assignments) aren't hoisted (within the same scope, see second case explanation), of course. So your snippet is translated to
(function()
{
var currentSize;//undefined
if (currentSize == 'hi')//always false
{
currentSize = 'hello';//assignment that will never be
}
alert(currentSize);//alerts undefined, of course
}());
By leaving out the var, proceeds to scope scanning (checking for the variable being declared in the global scope). Sadly, in doing so, the context of the first use of the var is lost (inside a branch), and the assignment is hoisted, too. An implied global is translated to:Thank god this isn't true. I assumed it was, because I tested a couple of things in the console that seemed to corroborate this. In this case #amadan is right: you're using the global variable (called greeting in your snippet by mistake when I posted this). I'm going to leave the code below (corrected it) to show what implied globals actually are, hoping it helps somebody sometime in understanding scopes/scope-scanning in JS.
var currentSize = 'hello';
//well, actually implied globals can be deleted, so it's more like
Object.defineProperty(this,'currentSize',{value:undefined,
writable:true,
enumerable:true,
configurable:true});
(function()
{
if (currentSize == 'hi')//always false
{//this still doesn't get executed
currentSize = 'hello';//assignment that will never be
}
alert(currentSize);//alerts undefined
}());
In your first code snippet, you're checking global variable, which doesn't exist -> doesn't pass if condition.
Check out this on javascript scopes and how to work with variables Javascript garden - function scopes
Related
Today, I got completely surprised when I saw that a global variable has undefined value in a certain case.
Example:
var value = 10;
function test() {
//A
console.log(value);
var value = 20;
//B
console.log(value);
}
test();
Gives output as
undefined
20
Here, why is the JavaScript engine considering global value as undefined? I know that JavaScript is an interpreted language. How is it able to consider variables in the function?
Is that a pitfall from the JavaScript engine?
This phenomenon is known as: JavaScript Variable Hoisting.
At no point are you accessing the global variable in your function; you're only ever accessing the local value variable.
Your code is equivalent to the following:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Still surprised you're getting undefined?
Explanation:
This is something that every JavaScript programmer bumps into sooner or later. Simply put, whatever variables you declare are always hoisted to the top of your local closure. So, even though you declared your variable after the first console.log call, it's still considered as if you had declared it before that.
However, only the declaration part is being hoisted; the assignment, on the other hand, is not.
So, when you first called console.log(value), you were referencing your locally declared variable, which has got nothing assigned to it yet; hence undefined.
Here's another example:
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
What do you think this will alert? No, don't just read on, think about it. What's the value of test?
If you said anything other than start, you were wrong. The above code is equivalent to this:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
so that the global variable is never affected.
As you can see, no matter where you put your variable declaration, it is always hoisted to the top of your local closure.
Side note:
This also applies to functions.
Consider this piece of code:
test("Won't work!");
test = function(text) { alert(text); }
which will give you a reference error:
Uncaught ReferenceError: test is not defined
This throws off a lot of developers, since this piece of code works fine:
test("Works!");
function test(text) { alert(text); }
The reason for this, as stated, is because the assignment part is not hoisted. So in the first example, when test("Won't work!") was run, the test variable has already been declared, but has yet to have the function assigned to it.
In the second example, we're not using variable assignment. Rather, we're using proper function declaration syntax, which does get the function completely hoisted.
Ben Cherry has written an excellent article on this, appropriately titled JavaScript Scoping and Hoisting.
Read it. It'll give you the whole picture in full detail.
I was somewhat disappointed that the problem here is explained, but no one proposed a solution. If you want to access a global variable in function scope without the function making an undefined local var first, reference the var as window.varName
Variables in JavaScript always have function-wide scope. Even if they were defined in the middle of the function, they are visible before. Similar phenomena may be observed with function hoisting.
That being said, the first console.log(value) sees the value variable (the inner one which shadows the outer value), but it has not yet been initialized. You can think of it as if all variable declarations were implicitly moved to the beginning of the function (not inner-most code block), while the definitions are left on the same place.
See also
Javascript function scoping and hoisting
Javascript variable declarations at the head of a function
There is a global variable value, but when control enters the test function, another value variable is declared, which shadows the global one. Since variable declarations (but not assignments) in JavaScript are hoisted to the top of scope in which they are declared:
//value == undefined (global)
var value = 10;
//value == 10 (global)
function test() {
//value == undefined (local)
var value = 20;
//value == 20 (local)
}
//value == 10 (global)
Note that the same is true of function declarations, which means you can call a function before it appears to be defined in your code:
test(); //Call the function before it appears in the source
function test() {
//Do stuff
}
It's also worth noting that when you combine the two into a function expression, the variable will be undefined until the assignment takes place, so you can't call the function until that happens:
var test = function() {
//Do stuff
};
test(); //Have to call the function after the assignment
The simplest way to keep access to outer variables (not just of global scope) is, of course, to try to not re-declare them under the same name in functions; just do not use var there. The use of proper descriptive naming rules is advised. With those, it will be hard to end up with variables named like value (this aspect is not necessarily related to the example in the question as this variable name might have been given for simplicity).
If the function might be reused elsewhere and hence there is no guarantee that the outer variable actually defined in that new context, Eval function can be used. It is slow in this operation so it is not recommended for performance-demanding functions:
if (typeof variable === "undefined")
{
eval("var variable = 'Some value';");
}
If the outer scope variable you want access to is defined in a named function, then it might be attached to the function itself in the first place and then accessed from anywhere in the code -- be it from deeply nested functions or event handlers outside of everything else. Notice that accessing properties is way slower and would require you to change the way you program, so it is not recommended unless it is really necessary: Variables as properties of functions (JSFiddle):
// (the wrapper-binder is only necessary for using variables-properties
// via "this"instead of the function's name)
var functionAsImplicitObjectBody = function()
{
function someNestedFunction()
{
var redefinableVariable = "redefinableVariable's value from someNestedFunction";
console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('--> redefinableVariable: ', redefinableVariable);
}
var redefinableVariable = "redefinableVariable's value from someFunctionBody";
console.log('this.variableAsProperty: ', this.variableAsProperty);
console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('redefinableVariable: ', redefinableVariable);
someNestedFunction();
},
functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
functionAsImplicitObject();
// (spread-like operator "..." provides passing of any number of arguments to
// the target internal "func" function in as many steps as necessary)
var functionAsExplicitObject = function(...arguments)
{
var functionAsExplicitObjectBody = {
variableAsProperty: "variableAsProperty's value",
func: function(argument1, argument2)
{
function someNestedFunction()
{
console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
functionAsExplicitObjectBody.variableAsProperty);
}
console.log("argument1: ", argument1);
console.log("argument2: ", argument2);
console.log("this.variableAsProperty: ", this.variableAsProperty);
someNestedFunction();
}
};
return functionAsExplicitObjectBody.func(...arguments);
};
functionAsExplicitObject("argument1's value", "argument2's value");
I was running into the same problem even with global variables. My problem, I discovered, was global variable do Not persist between html files.
<script>
window.myVar = 'foo';
window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>
I tried to reference myVar and myVarTwo in the loaded HTML file, but received the undefined error.
Long story/day short, I discovered I could reference the variables using:
<!DOCTYPE html>
<html lang="en">
<!! other stuff here !!>
<script>
var myHTMLVar = this.parent.myVar
/* other stuff here */
</script>
</html>
I've been going through Javascript function scope and have run into this:
var scope = "global";
function f(){
console.log(scope);
var scope = "local";
console.log(scope);
}
f();
Now I understand that the output of the first log is "undefined" because of how js hoists variables at the top of the function. BUT when I remove var from "var scope = "local";" the first log outputs "global" and this has got me scratching my head. Can someone explain why that is happening please? I mean doesn't js sequentially run the code? As such how can removing VAR have any impact on the first log?
Two-pass parsing. The code will be treated as if it was
function f() {
var scope; // var created, but no value assigned. this overrides the earlier global
console.log(scope);
scope = 'local';
console.log(scope);
}
The var's CREATION will be executed as if it was the very first bit of code executed in the function. But the actual assignment operation won't occur until where it normally would.
Javascript sometimes behaves a bit differently from other languages. Take a look at http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html, they explain it a bit.
If you omit the var statement, the first log uses the global variable, which is set with the string "global". There is no other local variable and no hoisting.
First log: global variable scope set with "global" content
Assignment of new string for the same global variable
Second log: global variable scope set with "local" content
I have been taught to define variables at the top, regardless of their position in your code, as this is how JavaScript will interpret things. So, my understanding is that:
var foo = "Bob";
if (2 + 2 === 4) {
var car = "Blah";
}
Will be interpreted like:
var foo = "Bob",
car;
if (2 + 2 === 4) {
car = "Blah";
}
Is my understanding correct? I've always tried to position my variable definitions at the top of the current scope, but sometimes those variables are only needed inside an if statement, so defining them outside seems a bit odd - is this still best practice?
Yes. var statements are hoisted (which is why best practise is to use them at the top of the function — it avoids confusion from people assuming block scope instead of function scope)
Yes. Variable and function declarations are hoisted to the top of the scope in which they are defined. Since JavaScript only has function scope (and not block scope), the top of the scope in your example is outside the if statement.
Note that since function expressions are effectively just variable declarations, the function itself is not hoisted (as assignment occurs where you intend it to). That means it's only available after the assignment...
sayHello(); //Uh-oh... TypeError, undefined is not a function!
var sayHello = function() {
console.log("Hi!");
};
... as opposed to a function declaration, which can be used before it is defined in the source:
sayHello(); //"Hi!"
function sayHello() {
console.log("Hi!");
}
Yes, it is always good to define them on top of the scope (and javascript is function scoped), you could read more about this here.
Today, I got completely surprised when I saw that a global variable has undefined value in a certain case.
Example:
var value = 10;
function test() {
//A
console.log(value);
var value = 20;
//B
console.log(value);
}
test();
Gives output as
undefined
20
Here, why is the JavaScript engine considering global value as undefined? I know that JavaScript is an interpreted language. How is it able to consider variables in the function?
Is that a pitfall from the JavaScript engine?
This phenomenon is known as: JavaScript Variable Hoisting.
At no point are you accessing the global variable in your function; you're only ever accessing the local value variable.
Your code is equivalent to the following:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Still surprised you're getting undefined?
Explanation:
This is something that every JavaScript programmer bumps into sooner or later. Simply put, whatever variables you declare are always hoisted to the top of your local closure. So, even though you declared your variable after the first console.log call, it's still considered as if you had declared it before that.
However, only the declaration part is being hoisted; the assignment, on the other hand, is not.
So, when you first called console.log(value), you were referencing your locally declared variable, which has got nothing assigned to it yet; hence undefined.
Here's another example:
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
What do you think this will alert? No, don't just read on, think about it. What's the value of test?
If you said anything other than start, you were wrong. The above code is equivalent to this:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
so that the global variable is never affected.
As you can see, no matter where you put your variable declaration, it is always hoisted to the top of your local closure.
Side note:
This also applies to functions.
Consider this piece of code:
test("Won't work!");
test = function(text) { alert(text); }
which will give you a reference error:
Uncaught ReferenceError: test is not defined
This throws off a lot of developers, since this piece of code works fine:
test("Works!");
function test(text) { alert(text); }
The reason for this, as stated, is because the assignment part is not hoisted. So in the first example, when test("Won't work!") was run, the test variable has already been declared, but has yet to have the function assigned to it.
In the second example, we're not using variable assignment. Rather, we're using proper function declaration syntax, which does get the function completely hoisted.
Ben Cherry has written an excellent article on this, appropriately titled JavaScript Scoping and Hoisting.
Read it. It'll give you the whole picture in full detail.
I was somewhat disappointed that the problem here is explained, but no one proposed a solution. If you want to access a global variable in function scope without the function making an undefined local var first, reference the var as window.varName
Variables in JavaScript always have function-wide scope. Even if they were defined in the middle of the function, they are visible before. Similar phenomena may be observed with function hoisting.
That being said, the first console.log(value) sees the value variable (the inner one which shadows the outer value), but it has not yet been initialized. You can think of it as if all variable declarations were implicitly moved to the beginning of the function (not inner-most code block), while the definitions are left on the same place.
See also
Javascript function scoping and hoisting
Javascript variable declarations at the head of a function
There is a global variable value, but when control enters the test function, another value variable is declared, which shadows the global one. Since variable declarations (but not assignments) in JavaScript are hoisted to the top of scope in which they are declared:
//value == undefined (global)
var value = 10;
//value == 10 (global)
function test() {
//value == undefined (local)
var value = 20;
//value == 20 (local)
}
//value == 10 (global)
Note that the same is true of function declarations, which means you can call a function before it appears to be defined in your code:
test(); //Call the function before it appears in the source
function test() {
//Do stuff
}
It's also worth noting that when you combine the two into a function expression, the variable will be undefined until the assignment takes place, so you can't call the function until that happens:
var test = function() {
//Do stuff
};
test(); //Have to call the function after the assignment
The simplest way to keep access to outer variables (not just of global scope) is, of course, to try to not re-declare them under the same name in functions; just do not use var there. The use of proper descriptive naming rules is advised. With those, it will be hard to end up with variables named like value (this aspect is not necessarily related to the example in the question as this variable name might have been given for simplicity).
If the function might be reused elsewhere and hence there is no guarantee that the outer variable actually defined in that new context, Eval function can be used. It is slow in this operation so it is not recommended for performance-demanding functions:
if (typeof variable === "undefined")
{
eval("var variable = 'Some value';");
}
If the outer scope variable you want access to is defined in a named function, then it might be attached to the function itself in the first place and then accessed from anywhere in the code -- be it from deeply nested functions or event handlers outside of everything else. Notice that accessing properties is way slower and would require you to change the way you program, so it is not recommended unless it is really necessary: Variables as properties of functions (JSFiddle):
// (the wrapper-binder is only necessary for using variables-properties
// via "this"instead of the function's name)
var functionAsImplicitObjectBody = function()
{
function someNestedFunction()
{
var redefinableVariable = "redefinableVariable's value from someNestedFunction";
console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('--> redefinableVariable: ', redefinableVariable);
}
var redefinableVariable = "redefinableVariable's value from someFunctionBody";
console.log('this.variableAsProperty: ', this.variableAsProperty);
console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('redefinableVariable: ', redefinableVariable);
someNestedFunction();
},
functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
functionAsImplicitObject();
// (spread-like operator "..." provides passing of any number of arguments to
// the target internal "func" function in as many steps as necessary)
var functionAsExplicitObject = function(...arguments)
{
var functionAsExplicitObjectBody = {
variableAsProperty: "variableAsProperty's value",
func: function(argument1, argument2)
{
function someNestedFunction()
{
console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
functionAsExplicitObjectBody.variableAsProperty);
}
console.log("argument1: ", argument1);
console.log("argument2: ", argument2);
console.log("this.variableAsProperty: ", this.variableAsProperty);
someNestedFunction();
}
};
return functionAsExplicitObjectBody.func(...arguments);
};
functionAsExplicitObject("argument1's value", "argument2's value");
I was running into the same problem even with global variables. My problem, I discovered, was global variable do Not persist between html files.
<script>
window.myVar = 'foo';
window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>
I tried to reference myVar and myVarTwo in the loaded HTML file, but received the undefined error.
Long story/day short, I discovered I could reference the variables using:
<!DOCTYPE html>
<html lang="en">
<!! other stuff here !!>
<script>
var myHTMLVar = this.parent.myVar
/* other stuff here */
</script>
</html>
Just now,I saw some code like this:
if(condition){
var xx='sss';
}
//do something
if(condition){
console.info(xx);
}
Now, I just wonder why the second if statement work? How can it access the xx variable since it is a local variable defined in another if statement?
var in JavaScript is scoped to the containing execution context (e.g., the whole function's scope, or the whole global scope if the var is at global scope), not the block. JavaScript doesn't (yet) have block scope (ECMAScript6 looks likely to add it, via the new let keyword).
The code you've quoted is exactly equivalent to this:
var xx;
if(condition){
xx='sss';
}
//do something
if(condition){
console.info(xx);
}
This is covered by Section 10.5 of the specification, which describes what the engine does when entering a new execution context. (It's basically a two-phase process, first setting up all the declarations, and then executing step-by-step code.)
More: Poor misunderstood var
In JavaScript the scope is exacted to the closure (the last enclosing function block, or defaults to the window object). When a variable is declared anywhere within that function block it is hoisted to the top of the scope, so in essence a variable exists as undefined starting at the very top of the scope if it is declared anywhere in the scope.
Think of it like this, when the code begins executing it scans all the instructions for declarations and allocates the symbol name starting immediately.
console.log(x); // undefined
console.log(y); // error: Uncaught ReferenceError: y is not defined
var x;
for that matter you can take it to extremes:
console.log(x); // undefined, not an error
while (false) {
if (false) {
var x;
}
}
even though var x can't possibly be reached, and during execution would be optimized away completely. the engine will still hoist the variable to the top of the scope
hope this helps -ck
useful link: http://www.youtube.com/watch?v=taaEzHI9xyY&feature=youtu.be#t=42m57s
var declarations affect the entire scope of the smallest containing function or program. JavaScript is not block scoped.
Crock says:
Variable Declarations
All variables should be declared before used. JavaScript does not require this, but doing so makes the program easier to read and makes it easier to detect undeclared variables that may become implied globals. Implied global variables should never be used.
The var statements should be the first statements in the function body.
It is preferred that each variable be given its own line and comment. They should be listed in alphabetical order.
var currentEntry; // currently selected table entry
var level; // indentation level
var size; // size of table
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.
Use of global variables should be minimized. Implied global variables should never be used.
Note, this is changing with the let statement, and in current JavaScript (EcmaScript 5), the variable name in a catch block is block scoped.
javascript doesn't have block scope, so var xx='sss' is either locally scoped (if your sample code is inside a function) or globally scoped (if your sample code is not contained in a function).
JavaScript is a dynamic language, that isn't always picky about things like variable scoping. So, this "feature" allows you to write the code
if (condition) {
var xx = 'sss';
} else {
var xx = 'ttt';
}
// do something
if (condition) {
console.info(xx);
}
I would recommend avoiding this, since it makes your program harder to understanda and reason about.
If you declare a variable using var within a function, the variable is scoped to within that function. An if statement is not a function.
I'm assuming in this case both if statements are within the same function and therefore xx is in scope?
If a variable is declared inside a conditional statement, it is still available anywhere following the declaration in the containing function (or globally if the conditional is not in a function). However, it will equal undefined if the condition evaluates to false, unless the variable is later assigned a value.
If a local variable is referenced globally or in another function, a JavaScript error will occur. Depending on your browser, the error may say the variable "is not defined" or "is undefined". This is different from the variable equaling undefined like mentioned above, because a variable that equals undefined can still be referenced.