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>
Related
I initialized a variable of function through "." outside the function. Regarding closure rule, it should be set inside the function scope and after execution should be gone. But
Why after calling the function variable still exists?
Why can I access inside a function only through "."?
I initialized variable outside of the function through "." like f1.a = "any variable".
I checked if the variable of function initialized outside is accessible inside a function without ".":
I tried to get access to the variable inside the function. It seems if I get access to the variable by itself without "." it gives me an error "variable is not defined".
I checked if the variable of function initialized outside will be gone after function execution:
I call function and check if the value of the variable after execution is still available. Yes, it was still there.
f1.a = "any variable";
function f1(){
(function()
{
console.log(a);
}()) // a is not defined
}
f1();
console.log(f1.a); // after f1(), f1.a still exist
I expected variable "a" visible by itself inside the "f1" since I initialized inside the function scope f1.a = "any variable", but I can get access only with "."
I expected variable "a" will be gone after execution f1(), but it still exists
There are several things you need to understand to get a clear idea of what's happening here. First, JavaScript hoists function definitions to the top of the file.
Knowing that, you could imagine your code is something like this once JavaScript interprets it:
var f1 = function (){
(function()
{
console.log(a);
}()) // a is not defined
}
f1.a = "any variable"
f1();
console.log(f1.a);
Secondly, in your first console.log(a) you are referencing a variable a which was never declared. If you change that to console.log(f1.a) you'll see the value of f1.a as expected.
It's also not clear why you are using an immediately invoked function inside of your f1 function. It makes analyzing this code even more complex. It seems like you are trying to get a better understanding of how closures work? But for closures you should be interested in variables declared inside of f1, rather than properties of f1. For example, something like this.
f1 = function (){
var a = 'something'
return function()
{
console.log(a);
}
}
var closure = f1();
// f1 is finished running here.
closure(); // closure still has access to f1's variable.
I think the three areas you could learn more about to understand the code above are 1. Scope and especially hoisting 2. Objects, this and object properties and 3. Closures.
I have read this excellent piece:
How do JavaScript closures work?
But I still have questions...
StackOverflow tags defines "Closures" as "A Closure is a first class function that refers to (closes over) variables from the scope in which it was defined. If the closure still exists after its defining scope ends, the variables it closes over will continue to exist as well".
When my functions end with a return, do all variables defined within them get deleted and memory allocation made free? It appears to me that conditions exist when one could answer yes or no.
Yes, I know memory is plentiful and free but that is not my question.
Think of an intranet based application which ajax's data back and forward during a working day. As data is received by the client, massaged, and no longer required, it can be dispensed with. Poor programming technique could result in a build up of data no longer user or required (or worse, data being used that is out of sync).
Reading the piece on closures above tells me that some variables within a function can still be referenced from outside the function scope. I am guessing that I will need to re-read the closure document several times as perhaps I've not fully understood it.
Can someone share an example on where a variable continue to exist?
And... Do I gain any benefit if, at the begining of each function I declare my variables, and prior to using a "return true;" if I reassign the variables a value of null?
All help appreciated...
num will exist within a function outside of IIFE c which returns a
var c = (function() {
var num = 10;
var a = function a(n) {
console.log(n)
return n * Math.random()
};
return a.bind(null, num)
}());
var res = [c(), c(), c()];
console.log(res)
When you return true from a function you don't have a closure. You have a closure when you return a function from a function, like this:
function makeAdder( firstNumToAdd ){
return function( secondNumToAdd ){
return mainNumToAdd + secondNumToAdd;
}
}
var add3 = makeAdder(3);
var seven = add3(4);
In the above example, the variable firstNumToAdd, although is scoped to the function makeAdder is always accessible to the inner function, even though the function exited after executing the line var add3 = makeAdder(3);
Please correct me if I am wrong, but I don't think that
some variables within a function can still be referenced from outside the function scope
is true.
Closures remember the context of a function call and bind the relevant variables (i.e. this, etc) to the closure's scope. If that is correct, then calling the closure again would cause the old variables' values to get garbage collected and you wouldn't have to worry about memory waste or out of sync data.
obj = {
func: function(){
var test = 'preserved value';
this.func2 = function(){
console.log(test);
};
}
};
obj.func();
obj.func2(); //'preserved value'
obj.func = undefined;
obj.func2(); //'preserved value'
As you can see, even after completely blowing away the original scope (func) the enclosing scope retains the test var.
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>
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
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.