How does JavaScript interpret variables in a scope? - javascript

I have a little doubt in my mind, that how javascript interpreter works! Specially for the case I am mentioning here.
var a = 5;
function foo(){
debugger
a = 100;
if(false){
var a = 10;
}
a = 1000;
}
foo();
console.log(a);
Simply copy and paste the above code in browser's console, the expected answer is 1000.
What it returns is 5.
I have put debugger knowingly, when it hits debugger, the SCOPE section shows variable a as in scope with undefined. So the further assignment is done to local variable a, that was not meant to be created at all, as it is in false block.
I am aware about the scope of variable is not limited to {}, but it is limited to function. But this case is a surprise!
Can anyone explain this?

var declarations encompass the entire scope.
function() {
a = 5;
var a = 10;
}
is equivalent to:
function() {
var a;
a = 5;
a = 10;
}
So a is a local variable in the scope of the function, not global in the first part and local afterwards (that would be so complicated!).

Little formatting to your code.
var a = 5;
function foo(){
debugger
a = 100;
if(false){
var a = 10;
}
a = 1000;
}
foo();
console.log(a);
Hoisting is the keyword you are looking for. Javascript hoists the variables. Means it puts the declaration of your local variables at top of functions irrespective its going to be executed or not. So after interpretation your code looks like following.
var a = 5;
function foo(){
var a;
debugger
a = 100;
if(false){
a = 10;
}
a = 1000;
}
foo();
console.log(a);
So a becomes a local variable inside function, hence change of value inside function only changes local variable and not global. Hence console prints 5, global variable, value of which was never changed.

It's because you have a var a in the code. All declarations in the function are hoisted to the top, even if it looks like var a = 10; will never execute. Remove var and it will behave as expected.

Related

Why I cannot access this variable inside this simple function?

Im new to Javascript and I dont understand whats happening how the scope works in the following codes:
//Code n°1:
let num = 1;
function test(){
console.log(num);
}
test() //As I expected I get in console "1"
//Code n°2:
let num = 1;
function test(){
let num = 2;
console.log(num);
}
test() //As I expected I get in console "2"
But here is the problem:
//Code n°3:
let num = 1;
function test(){
console.log(num)
let num = 2;
console.log(num);
}
test() //I expected to get in console "1" and then "2" but instead I get an error.
When I run in my browser the code n° 3 Im getting this error:
Uncaught ReferenceError: can't access lexical declaration 'num' before initialization
If I look at code 1 and 2 I suppose that after running code 3 I will get in my console the number 1 and then number 2. But that isnt happening. Why?
Thank you so much!
The JavaScript exception "can't access lexical declaration `variable' before initialization" occurs when a lexical variable was accessed before it was initialized. This happens within any block statement, when let or const declarations are accessed before they are defined.
When the 'test function' is called, the function body gets executed. When it reaches to 'console.log(num)', it starts to search for the 'num variable declaration' (or 'num assignment' or 'num reassignment) in the test function scope. So it finds the variable declaration in the next line. The priority for searching for a variable is:
Current Scope
Closure
Scope Chain
So, when the variable declaration is found i the current scope, searching is not continued to scope chain. (It will not search for num variable in global scope.)
I guess this is the reason of the error, but as I am new to javaScript, I am not completely sure.
If you initialize the variable without redeclaring, it will work normally, now if you redeclar the variable, then this error will happen with let or var, because in the same scope, it is being called by the previous console.log().
It is not recommended that you re-declare let variables, if you need to, don't use the same in previous statement, note that if you comment on the first console.log(), the code will work again, this occurs exactly for the lifetime of the variable within this scope, in this case, let
let num = 1;
function test(){
console.log(num)
let num = 2;
console.log(num);
}
test()
Bacause in javascript functions and variables declared with var are hoisted, moved up to the top of the function/lexical block.
But let keyword respect the lexical flow. It pins variable where it is declared.
console.log try to access a variable that is declared bellow in the flow.
So move up manually your let declaration to be above de console.log or change it to var let num = 2; to let javascript engine move it above all automatically.
var num = 1;
function test(){
let num = 2;
console.log(num);
}
test()
or
var num = 1;
function test(){
console.log(num)
var num = 2;
console.log(num);
}
test()
or, to get access to global var num, which is shadowed by declarations from test's body, var or let, no matter
var num = 1;
function test(){
console.log(num)
//let num = 2;
console.log(num);
}
test()

Why is non-static variable behaving like static?

function emergency() {
var ambulance = 100;
var callAmbulance = function() { alert(ambulance); }
ambulance++;
return callAmbulance;
}
var accident = emergency();
accident(); // alerts 101
I am referring to the variable 'ambulance'.
When I call accident(); it should call emergency() which should use the declared variable 'ambulance' [considering the global scope thing in javascript, still it could set the value to global] but its using old value 101 instead of setting again back to 100 - behaving more like static var.
What's the Explanation?
What you have there is called a closure. It means a function can access variables declared in outer functions (or in the global scope). It retains access even after the 'parent' function has returned. What you need to understand is that you don't get a copy. The changes performed on that variable are visible to your inner function, which in theory means you could have multiple functions sharing access to the same variable.
function f () {
var x = 0;
function a() { x += 1; }
function b() { x += 2; }
function show() { console.log(x); }
return {a:a, b:b, show:show};
}
var funcs = f();
funcs.a();
funcs.b();
funcs.show(); // 3
One thing to be aware of is that a subsequent call to f will create a new scope. This means a new x variable will be created (new a, b, show functions will be created as well).
var newFuncs = f();
newFuncs.a();
newFuncs.show(); // 1
funcs.a();
funcs.show(); // 4
So, how do you get a copy? Create a new scope.
function g () {
var x = 0;
var a;
(function (myLocal) {
a = function () { myLocal += 1; }
}(x));
x += 200;
return a;
}
JS only has pass-by-value so when you call the anonymous function, the xvariable's value will be copied into the myLocal parameter. Since a will always use the myLocal variable, not x, you can be certain that changes performed on the x variable will not affect your a function.
If, by any chance, you're coming from a PHP background you are probably used to do something like
use (&$message)
to allow modifications to be reflected in your function. In JS, this is happening by default.
You are creating a function definition which is not compiled at this time:
var callAmbulance = function() { alert(ambulance); }
And before you are sending it to the called function, you are incrementing the num:
ambulance++;
And then you are sending it to the called function:
return callAmbulance;
But whether you are sending it there or not, it doesn't matter. The below statement executes or compiles the function:
var accident = emergency();
And this takes in the current ambulance value which is 101 after the increment. This is an expected behaviour in creating function but not executing it. Please let me know, if you didn't understand this behaviour. I will explain it more clearly.

Declaring two variable with the same name

Is it possible to call the same name variables which set outside of the function?
var a = $(window).width(); // I want to call this variable
if(!$.isFunction(p)){
var a = $(window).height(); // Not this one
alert(a);
}
FIDDLE
In this case, you have actually redefined the value of a. There is absolutely no way of referencing a different variable with the same name, as it just acts as a redefinition.
If you want to declare a global variable you can do so by
window.varname="This is a global variable";
And you can access the same by
alert(window.varname);
Now you can also have a local variable inside a function with the same name
var varname="This is a local variable";
And you can access it normally.
Here's your code so that you can access the global variable not the local one.
var p = ['foo',''];
window.a = $(window).width();
if(!$.isFunction(p)){
var a = $(window).height();
alert(window.a);
}
In a code snippet such as yours, the variable a is being redefined. This is because an if statement doesn't create another scope for variables. However, functions do.
In a case like this:
var a = 0; // global
function doStuff() {
var a = 10; // local
alert(a);
alert(window.a)
}
alert(a);
doStuff();
alert(a);
inside the function doStuff, the variable a is being redefined. This snipped will therefore alert the numbers 0, 10, 0, 0. This proves that the global variable is not redefined inside the function, as printing a after calling doStuff doesn't change the value of a in the global scope.
The variable a outside of the function can be accessed, as any variable not declared in a non-global scope is placed inside the window object. However, if using this snippet (which calls an anonymous function, creating a new scope):
var a = 0; // global
function doStuff() {
var a = 10; // local
alert(a);
alert(window.a)
function() {
var a = 20; // even more local
alert(a);
alert(window.a);
}();
}
alert(a);
doStuff();
alert(a);
you cannot access the value of a inside the doStuff function. You can still access the global variable using window.a.
In your case, however, the if statement does not create a new scope, therefore you are redefining the variable a to the new value $(window).height().
Example:
var a=10;
if(true){
var a=5;
}
alert(a)// it will return a=5;
var a=10;
var a=5;
//both are same way assign value
In js if statement is not scope it visible every where with in function . you have to change the variable name
There is no blockscope in JavaScript (at least up until ES6).
Like you seem to expect from the if block. See
What is the scope of variables in JavaScript?
for an excellent summary of scopes that do exist in JavaScript.
Beware of Hoisting
Furthermore, you shouldn't sprinkle your var declarations through your code, but explicitly put them in the top of your function. That is where Javscript will hoist them anyway:
# so if you have a function like this
var i = 5;
function testvar () {
alert(i);
var i=3;
}
testvar();
# the alert window will contain undefined.
# because internally, it's been changed into this:
var i = 5;
function testvar () {
var i;
alert(i);
i=3;
}
testvar();
Minimize use of the global scope
Read
What is meant by “leaking” into global scope?
And listen to what Doug Crockford has to say about it. Actually, take an hour and watch the whole talk.
You can do it like this:
var p = ['foo',''];
var a = $(window).width(); // I want to call this variable
if(!$.isFunction(p)){
(function(b){
var a = $(window).height();
alert(b);
})(a);
}
No need to use the global scope, just create an anonymous function and call it with a as the argument. Inside the function b is a reference to the a variable outside the function.
It is a good practice not to modify the window object in javascript to write clean and maintainable code.
Less bugs and problems. I mean, never do the window.a thing. Is evil for your code.
No, you can't because of you have redefined the variable name in the same scope and beacuse of the hoisted variables your code will be interpreted by javascript in the following mode:
var p, a;
p = ['foo',''];
a = $(window).width(); // I want to call this variable
if(!$.isFunction(p)){
a = $(window).height(); // Not this one
alert(a);
}
Now you can easly see that the variable a will be replaced and not created
JavaScript has two scopes: global and local. In your example a is in the global scope both times so you are just redefining it.
However you can specify skip a variable in local scope and get the one from global. Consider this example:
var a = 1;
function foo () {
var a = 2;
console.log("var a is: " + window.a);
console.log("local var a is: " + a);
}
foo ();
Will log "var a is: 1"\n"local var a is: 2\n" to the console. This is about as close as it gets to what you need
var abc = new Array();
abc[0] = 'str1';
abc[1] = 'str2';
Use array in this case
Try this (pattern)
var p = ['foo', ''];
var a = function (name) {
return (
name === "height"
? $(window).height()
: (name === "width" ? $(window).width() : name)
)
};
if (!$.isFunction(p)) {
// `$(window).width()` , `$(window).height()`
alert( a("width") + "\n" + a("height") );
}
jsfiddle http://jsfiddle.net/guest271314/2tuK4/

Minimum viable JavaScript closure

The top answer on Stack Overflow regarding JavaScript closures defines them as (paraphrase):
A function that simply accesses variables outside of your immediate lexical scope that are not deallocated after the function returns.
Based strictly on this definition it seems like we can reduce the minimum viable closure (that does something useful) to a function containing no local variables that alters something in the global scope:
var x = 0;
function foo() {
x = x + 1;
return x;
}
foo(); // returns 1
foo(); // returns 2, et cetera
However, usual examples contain nested functions and variables that are "enclosed". Is this example a closure (or perhaps the global scope is now the closure)? Or is the definition incomplete?
Thanks!
No, because you simply don't use a closure here, there's only one scope, the global one, you're just using a global variable.
The definition you cite is ambiguous there :
after the function returns
In this definition, the "function" which "returns" is the one whose scope holds the variable, that is the external one. As there's no outer function, and the global scope never ends, there's no closure.
A closure would be like this :
var foo = (function(){
var x = 0;
return function() {
x = x + 1;
return x;
}
})(); // the function returns but x keeps being usable by the inner function
foo(); // returns 1
foo(); // returns 2, et cetera
And a less useless closure would be like this :
function makeFoo(){
var x = 0;
return function() {
x = x + 1;
return x;
}
}
var foo1 = makeFoo(), foo2 = makeFoo();
foo1(); // returns 1
foo2(); // returns 1
foo1(); // returns 2, et cetera
Yes, it is a closure. In the top answer, it actually provides a simplest closure example:
var a = 10;
function test() {
console.log(a); // will output 10
console.log(b); // will output 6
}
var b = 6;
test();
This is basically identical to your example, the scope of the function definition just happens to be the same as the parent of execution context.
When a Javascript function is invoked, a new execution context is created. Together with the function arguments and the parent object, this execution context also receives all the variables declared outside of it (in the above example, both 'a' and 'b').
You can actually use a new scope to replace the global scope in your example:
(function(){
var x = 0;
function foo(){
x = x + 1;
console.log(x);
}
foo(); // output 1
foo(); // output 2
})();

Javascript variable declarations at the head of a function

I've been told that javascript variables should all come before they are used in a function, such that:
function contrived() {
var myA, myB;
myA = 10;
myB = 20;
return myA + myB;
}
Is prefered over:
function furtherContrivance() {
var myA = 10;
var myB = 20;
return myA + myB;
}
Is this the case? And why is that?
I guess some people might prefer the former style because that's how it works inside. All local variables exist for the entire lifetime of the function, even if you use var to declare them in the middle of the function.
There's nothing wrong with declaring variables later in the function, syntax-wise, it might just be confusing as the variables will then exist before the line that declares them. Hence this function:
function bar() {
alert(foo); // Alerts "undefined". Not an error because the variable does exist.
var foo = 10;
alert(foo); // Alerts the value 10.
}
Is equivalent to this:
function bar() {
var foo;
alert(foo);
foo = 10;
alert(foo);
}
Another related fact is that nested function definitions (done using function foo() { ... }) will get moved to the top of the containing function as well, so they will be available even if the code that calls them comes before them.
Yes, the variable declaration should come at the top of the function:
function foo() {
var a, b;
}
However, initializing variables can be part of the declaration:
function foo() {
var a = 10, b = 20;
}
The reasoning behind declaring all variables at the top of the function where they are used is to avoid scope confusion.
Here is an example of bad code:
function foo() {
var b;
for (var i = 0; i < 5; i++) {
var a;
a = b = i;
setTimeout(function(){
console.log(a, b);
}, 1000);
}
}
If you execute the code, it will log 4, 4 5 times, rather than counting up. This is because only functions act as closures and introduce new scope. In JavaScript, any var declaration within a function gets executed at the beginning of the function.
This makes the above error much more visible:
function foo() {
var a, b, i;
for (i = 0; i < 5; i++) {
a = b = i;
setTimeout(function(){
console.log(a, b);
}, 1000);
}
}
There is no difference in this case between this two. I'd go with:
function furtherContrivance() {
var myA = 10,
myB = 20;
return myA + myB;
}
which is knows as single var pattern in javascript.
What you really need to take care of is defining your variables in the beginning of your functions. There is a thing in javascript called variables hoisting which means that variable definitions used in function "raise" on top. It's best described by an example:
var x = 'global'; // global (bounded to a global object which is window in browsers)
function func() {
alert(x); // undefined (you expected 'global', right?)
var x = 'local';
alert(x); // local
}
func();
what really happens is called (as I said) variables hoisting (definition of x raises on top), so the code above is actually the same as:
var x = 'global';
function func() {
var x; // definition of `x` raised on top (variables hoisting)
alert(x); // undefined in a local scope
x = 'local';
alert(x);
}
What a javscript interpreter does is it looks inside a function, gathers locally defined variables and raises them on top - this might be a good reason why you should use single var pattern.
In the example you give this is absolutely not the case. In a language like Javascript, it will be more of a developer preference, but it won't have any impact on the result.
Yes, place them at the top. It adds to code clarity.
Try this example:
var x = 1;
(function() {
x++;
alert( x ); // What will this alert show?
var x = 'done';
alert( x );
})();
Looks like it should alert 2, but it alerts NaN.
This is because the variable declaration is hoisted to the top, but the initialization stays in the same place.
So what is actually happening is:
var x = 1;
(function() {
var x;
x++;
alert( x ); // What will this alert show? NaN
x = 'done';
alert( x );
})();
...which makes the NaN expected.
For readability, it's definitely preferred.
However, Javascript "hoists" declarations. Hoisting means that vars and functions will be automatically moved to the top of their scope. This allows you to do things such as use a function before it's declared:
function myScope()
{
test();
function test()
{
//...
}
}
This can lead to some confusion, especially if variables within block scopes are declared. For example:
for(var i in foo)
{
var e = myFunc();
}
The declaration of e will be hoisted to the top of the closure, and e will be initialized to undefined. This allows for some interesting non-intuitive situations, such as:
if(!foo) //Will not throw reference error because foo is declared already
{
var foo = {};
}
So, regardless of how you declare your variables, they'll all get "moved up" to the top of the function anyway.
Hope this helps!

Categories