Why is this console log affected by a subsequent if statement? - javascript

Consider the following code:
var a = 'a';
function b() {
console.log(a);
if (!a) {
var a = 'b';
}
}
b();
Running b() prints undefined to the console. However, if you remove the if statement, or even simply remove the var keyword from the expression within the if statement so that you're redefining the outer a variable, the string a will be printed to the console as expected.
Can anyone explain this? The only cause I can think of is that this is a race condition, and the if statement is running just a tad faster than the console.log.

This is not a race condition - it's a language feature and is working as the designers of the Javascript language intended.
Because of hoisted variable definitions (where all variable definitions within a function scope are hoisted to the top of the function), your code is equivalent to this:
var a = 'a';
function b() {
var a;
console.log(a);
if (!a) {
a = 'b';
}
}
b();
So, the locally declared a hides the globally declared a and initially has a value of undefined until your if statement gives it a value.
You can find lots of discussion about this characteristic of the Javascript language by searching for "Javascript hoisting".

When you use var statement in a function, it will be creating a new variable which is local to that function.
All the variables declared in the function, will be moved to the top of the function, irrespective of the place where they are actually declared. This is called Hoisting.
The hoisted variables will have the value undefined, by default, till they are explicitly assigned a value.
It prints undefined as it is, because of the 3rd point.
In your case, you declared a variable a within the if block. Since the variables are hoisted, the declaration is moved to the top of the function. It has the same name as the outer variable. When you access a in the function, it first looks in the current scope if there is a variable by that name exists. It checks other scopes only if it is not found in local scope. So, the local a shadows the outer a. When you remove the var statement, there is no a in local scope, so the outer a is used. That is why it prints a.

Related

Calling context and lexical scoping

Consider the following code.I’m not sure I completely understand lexical scoping, but, unless I’m misinterpreting everything I’ve been reading, an inner function can only reference outer variables that have been declared in the same context.
let a = 7;
function test() {
return a;
}
function test2() {
let a = 5;
return test()
}
console.log(test2())
In other words, what matters is where such functions are born and NOT where they are called, which, in the case of my example code above, means that the variable “a” that test returns is not the same as the variable “a” declared and assigned the value of 5 in test2. It’s as if the “a” in test2 were foreign to test.
Am I on the right track or is there something I’m missing?
You seem to be on the right track.
Variables defined with let are local to whatever scope they are defined in, either the global scope or a function or a block inside a function. Code in a function or block can "see" variables declared within the same function or block, or variables in an outer scope such as an enclosing function or the global scope.
Your code has two distinct variables that happen to share the same name, a. Each let statement creates a brand new variable, at whatever scope the let statement appears in.
Because your two a variables are actually different and unrelated variables, we could rename them to have two different names, and the code will work the same. I will use global_a for one of them and test2_a for the other:
let global_a = 7;
function test() {
return global_a;
}
function test2() {
let test2_a = 5;
return test()
}
console.log(test2())
Now when you look at the code, you can see that test() only uses global_a. It doesn't even try to use test2_a. And in the test2() function, you can see that the test2_a variable is only assigned but never used!
Note that variables defined with var follow basically the same rules, except that inner blocks inside a function (like an if statement with curly braces) don't count. var only follows function scope and ignores blocks inside a function.

Do I have to use `var` when declaring a function in javascript?

Consider the following 2 functions:
cat = function() {
console.log("Meow");
}
and:
var dog = function() {
console.log("woof")
}
cat() -> "Meow"
dog() -> "Woof
They both work, except that cat is not declared using var. Does this mean that it is globally scoped? I would say that both functions are globally scoped. There is also the syntaxfunction cat(){...}, I guess that is similar to the first style, some sort of implicit variable binding...
Could somebody explain the difference between the styles for declaring functions, if there is any.
Variables
If you don't specify var, let or const it will get globally scoped
If you do specify var, let or const then it will get scoped to the nearest enclosing scope depending on that particular specifier
(var - will get scoped to the nearest enclosing function or global scope if not defined inside of a function)
(let & const - will get scoped to the nearest enclosing block)
Functions
Assigning a function as follows:
var dog = function() {
console.log("woof")
}
Means that the function will not be accessible until the line that it is declared on is reached during execution, i.e. you will only be able to execute this function from after the line on which it was declared.
Whereas declaring a function as follows:
function cat(){...}
Means that it will be moved to the top of the enclosing scope, so you will be able to call it from anywhere within the reachable scope even if it's earlier in code than the line on which you declared it on.
Not using var/let/const makes it implicitly global, which is generally regarded as a bad thing. If you use 'use strict', you'll get an error for any implicit globals. The biggest issue that arises with implicit global variables is that you may not know that you've made a global variable. For example:
(function() {
a = 5;
})();
// a doesn't exist right?
console.log(a); // 5... whoops!
No you don't. you can declare a function like so:
function foo(){
}
Then foo is automatically declared at the appropriate scope.
Its all a matter of scopes. where will the interpreter declare the function. Doing it the way you did it, without var will cause the interpreter to create a global variable automatically, and that is the highest scope possible, meaning that it is accessible everywhere in the code. That is considered a bad thing, since you normally wouldn't want to do that unless it is done intentionally, because of deep reasons which I can go into if you wish.
function foo(){
function bar(){
console.log("bar");
}
bar();
console.log(typeof bar);
}
foo();
bar(); // will throw an error since "bar" does not exist at this scope
Read all about function declaration

Variables with the same name, but the local scope variable isn't being used, why?

Why is the declaration of the variable 'a' not used in the output of the console log of the below code? Doesn't that go against the scope chain? The variable 'a' with the undefined value on line 4 surely should be used on the console output on line 5?
var a = 15;
function checkScope(a) {
var a;
console.log(a); // log outputs 15 and not undefined
}
checkScope(a);
I want to understand this behaviour. To confirm, this behaviour has nothing to do with hoisting or even scope (i.e. scope chain)?
I don't believe scope is relevant to explain this behaviour because 'a' exists locally (its value is 'undefined' as it's not initialised) within the function, hence the Javascript engine will not look to the outer lexical environment to find 'a' (which has a value of 15). Or as per the MDN page: "The scope of a variable declared with var is its current execution context", which in this case is the enclosing function 'a'.
I also don't believe this behaviour is a result of hoisting as far as I know.
So, in addition, this is what is stated on the MDN page? "If you re-declare a JavaScript variable, it will not lose its value."?
Off-topic question but, what's the reason behind MDN's statement above? Is it simply related to memory management? There's no point to logical reason to allocate a new memory address every single time a variable is declared?
Now, going back to my question:
Since they're re-declared, If I'm interpreting MDN correctly, does that mean line 3 is basically ignored? But I don't think so because both variable are declared in their own, separate execution contexts, so surely the variable 'a' in line 4 is being used/accessed? Because the scope chain would suggest that the closest local variable would be used (i.e. the local 'a' with a value of undefined), and in addition, I would best think that the MDN statement applies to only re-declared variables only within the same lexical environment, correct?
I am just trying to work out whether 'a' in line 4 is actually used when we refer to 'a' in line 5? Or is line 3 ignored, and thus line 5 will instead access (refer to) the 'a' in line 3 (i.e. checkScope's 'a' parameter)?
- The latter sort of implies the former
It makes sense if line 3 from the above code is simply ignored, because deleting it achieves the same result:
var a = 15;
function checkScope(a) {
console.log(a); // log outputs 15 and not undefined
}
checkScope(a);
Thanks.
You have declared a variable named a three times.
Once as a global on line 1: var a = 15;
Twice inside the function:
Once with a var statement on line 4: var a;
Once with an argument definition on line 3: function checkScope(a) {
The line 4 var has no effect. The variable is already declared.
Then you call checkScope(a); which passes the value of the global a (15) to the local a argument of checkScope. This is why it is 15 and not undefined.
Since they're re-declared, If I'm interpreting MDN correctly, does that mean line 3 is basically ignored?
var a is basically ignored.
But I don't think so because both variable are declared in their own, separate execution contexts
This is where you are being tripped up by having three and not two declarations of a. It declares the variable defined by the function argument name.
If you redeclare a JavaScript variable, it will not lose its value.
take a look at this answer.
var a = 15;
function checkScope(a) {
var a; // this is equivalent to redeclaring the variable
console.log(a); // log outputs 15 and not undefined
}
checkScope(a);
Basically line 4
var a;
is where the compiler finds a redeclared variable of the function parameter. Because of the missing initializing, a keeps the value of the handed over value of the parameter a.
Consider 'a' to be different assigned variable for arguments in function and inside as a local variable.
function a != var a;
Now, some 'a' is called by console.log(a). Now, it will search for the default 'a' value if existing, i.e, 'a' of function. Even on manipulation of 'a',
function hoist(a) {
var a;
a*=2;
console.log(a);
}
hoist(10);
output: 20

why if statement isn't looking for a variable in global scope

I am trying to understand scoping in JS.Here i have an example which has a variable in global scope called check.It has a truthy value i mean 1.Then inside a function called main which doesn't have a variable called check but has an if statement which checks whether there is a check variable or not ,upon which it reassign another value to check variable.Then prints it out.if i use
if(check){}
it prints undefined.Here i have some questions:
1. check variable is declared in global scope.It means it has access everywhere.Then even if the IF statement fails it should print the globally assigned value which is 1 instead of undefined.Why it prints undefined instead of 1??
2. main function scope doesn't have a check variable.When if fails to find check inside main function's scope ,why it doesn't look for it in global scope??
(function(){
var check=1;
function main(){
if(check){
var check=10;
}
document.write(check);
}
main();
})();
JavaScript only has function scope and something called hoisting. Every variable declaration inside a function gets put at the beginning of the function. Thus, your code is equivalent to
var check=1;
function main(){
var check; // = undefined
if(check){ // block doesn't create scope
check=10;
}
document.write(check);
}
The local variable check shadows the outer variable check.
JavaScript will look up the chain for the most local variable it can find. Basically, local scope trumps global scope.
This is one of the potential pitfalls with global scope, and is why it should be avoided, since it creates clashes with local variables.

Variables defined in global scope with identical names

Can anybody explain, why next js code rises two alert windows with 'string1' text rather than to rise the second with 'undefined' text inside?? If both variables are described in the same scope..
var a = 'string1';
alert(a);
var a;
alert(a);​
http://jsfiddle.net/FdRSZ/1/
Thanks
Variable declarations (and function declarations) are hoisted to the top of the scope in which they appear. Assignments happen in place. The code is effectively interpreted like this:
var a;
var a;
a = 'string1';
For example, consider what happens if you declare a variable inside an if statement body:
console.log(myVar); //undefined (NOT a reference error)
if (something === somethingElse) {
var myVar = 10;
}
console.log(myVar); //10
Because JavaScript does not have block scope, all declarations in each scope are hoisted to the top of that scope. If you tried to log some variable that was not declared, you would get a reference error. The above example is interpreted like this:
var myVar; //Declaration is hoisted
console.log(myVar);
if (something === somethingElse) {
myVar = 10; //Assignment still happens here
}
console.log(myVar);
So even if the condition evaluates to false, the myVar variable is still accessible. This is the main reason that JSLint will tell you to move all declarations to the top of the scope in which they appear.
In slightly more detail... this is what the ECMAScript 5 spec has to say (bold emphasis added):
For each VariableDeclaration and VariableDeclarationNoIn d in code, in
source text order do
Let dn be the Identifier in d.
Let
varAlreadyDeclared be the result of calling env’s HasBinding concrete
method passing dn as the argument.
If varAlreadyDeclared is false, then
Call env’s CreateMutableBinding concrete method passing dn and
configurableBindings as the arguments.
Call env’s SetMutableBinding concrete method passing dn, undefined, and strict as the arguments.
So, if a binding already exists with the identifier we are trying to bind now, nothing happens.
Thats because JavaScript does something called variable hoisting. In fact, your JS looks like this:
var a; // hoisted -> declared on top
a = 'string1';
alert(a);
alert(a);
See How Good C# Habits can Encourage Bad JavaScript Habits for more details on how JavaScript works.
That's because the second var a isn't a separate variable declaration. As far as the javascript interpreter is concerned it is the same a as the first one.
var means "Scope this variable to this function" not "Set the value of this variable to undefined".
If the variable is already scoped to the function then var foo means the same as just plain foo

Categories

Resources