How is variable declaration happening in javascript? - javascript

console.log(x)
var x = 10;
The console gives me undefined but I was expected reference error

var is the old way of declaring a variable in JS. It has a few properties that you should consider in comparison to what let and const let you do.
Your variable declaration var x = 10 will be raised up to the top of the script, as a function declaration on the other hand is hoisted by the JS engine during compilation. However, ONLY the declaration is hoisted and the variable will be assigned undefined until you assign to it another value manually using the assignment operator =.
JS engine doesn't throw a ReferenceError because your code is translated (using hoisting) into
var x;
console.log(x);
x = 10;
At the same time, var doesn't have the concept of block scope, so at the same time the code below is running just fine
console.log(x);
{
var x = 10;
}
The code above is being translated (using hoisting) into
var x;
console.log(x);
{
x = 10;
}

If a variable is used in code and then declared and initialized, the value when it is used will be its default initialization (undefined for a variable declared using var, use const and let to avoid this).

Related

Hoisting of let, const with separate initialization

In Documentaion said that let and const are hoisted.
Variables declared with let and const are also hoisted but, unlike var, are not initialized with a default value. An exception will be thrown if a variable declared with let or const is read before it is initialized.
Also there are a pretty clear sample there:
console.log(num); // Throws ReferenceError exception as the variable value is uninitialized
let num = 6; // Initialization
But I don't understand why this rise the ReferenceError also:
x = 9
console.log(x) // Why: "Throws ReferenceError exception as the variable value is uninitialized" here ?
let x;
Specs says 'let' supports hoising. Initialization is going before using this variable. What's wrong?
If the reason of the ReferenceError is TDZ then why in specs says that 'let' supports hoising, becouse of TDZ and hoising are mutually exclusive things... And what could be an example of hoising for 'let' if I can't use this varibable before declaration...
Why throws ReferenceError exception as the variable value is uninitialized here, in the console.log line?
It doesn't. It throws it on access of the variable, which includes assignment:
x = 9; // exception happens here already!
let x;
You cannot use an uninitialised variable at all. It only gets initialised when the let/const statement itself is evaluated.
What could be an example of hoisting for 'let' if I can't use this variable before declaration?
You cannot use it, but you can refer to it, e.g. by creating a closure over it:
function log() {
console.log(x);
}
// log(); - would fail here
let x = 9;
log();
Also it means that referring to x will actually refer to that uninitialised variable, not some other x from an outer scope. See Are variables declared with let or const hoisted? for more.

In the block scope even after initialisation with undefined var type of variable logs out unexpected value

function show() {
var x = 10;
if (true) {
var x = 20;
}
console.log(x); // 20
}
show();
But when I not initialise the 'x' manually which is inside the 'if-statement', it initialise with undefined and hoisted to the top and should log the most recent value which is undefined , as 20 is logs out in the above example. But it logs out 10.Why?
function show() {
var x = 10;
if (true) {
var x;
}
console.log(x); // 10
}
show();
From MDN - var:
Duplicate variable declarations using var will not trigger an error,
even in strict mode, and the variable will not lose its value, unless
another assignment is performed.
So, unless you re-assign any value to x, variable declared with var will keep its value.
Re-declaring x inside the if block does not create a new variable; x is created only once.
From the Ecmascript spec - 14.3.2 Variable Statement:
A var statement declares variables that are scoped to the running
execution context's VariableEnvironment. Var variables are created
when their containing Environment Record is instantiated and are
initialized to undefined when created. Within the scope of any
VariableEnvironment a common BindingIdentifier may appear in more than
one VariableDeclaration but those declarations collectively define
only one variable.
That is why x in the following statement
var x;
doesn't implicitly gets initialized with undefined; this re-declaration statement didn't re-create the variable x.
function show() {
var x = 10;
if (true) {
var x = undefined; // re-assigned
}
console.log(x);
}
show();
Note on hoisting: Unless you know this already, variables are NOT literally hoisted/moved to the top of the scope in which they are declared; variable declarations are processed before the code execution, this is why they appear to have moved to the top of the scope.
For more details, see: MDN - var hoisting
So if you are trying to relate above both example, you might confused. see both examples in different way, you will get the answer.

Why doesn't this assignment throw a ReferenceError?

Consider three cases, where both a and k are undefined:
if (a) console.log(1); // ReferenceError
and
var a = k || "value"; // ReferenceError
seems reasonable, but...
var a = a || "value"; // "value"
Why doesn't the last case throw a ReferenceError? Isn't a being referenced before it's defined?
This is because of one of var's "features" called hoisting. Per the link:
Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top. This also means that a variable can appear to be used before it's declared. This behavior is called "hoisting", as it appears that the variable declaration is moved to the top of the function or global code. (emphasis mine)
So, for example:
console.log(a);
var a = "foo";
Instead of throwing a ReferenceError as you might expect, since a is referenced before it is defined, it logs undefined. This is because, as mentioned earlier, the declaration is processed first and essentially happens at the top, which means it's the same as:
var a;
console.log(a);
a = "foo";
The same goes for functions as mentioned earlier:
function foo() {
console.log(a);
var a = "foo";
}
That's the same as:
function foo() {
var a;
console.log(a);
a = "foo";
}
To see why, look into the ECMAScript 2015 Language Specification:
13.3.2 Variable Statement
NOTE
A var statement declares variables that are scoped to the running execution context’s VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized to undefined when created.
[...]
A variable defined by a VariableDeclaration with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the VariableDeclaration is executed, not when the variable is created. (emphasis mine)
From this, we can gather that the var declarations are created before any code is executed (in their lexical environment) and are scoped to the containing VariableEnvironment, which is either in the global scope, or in a function. They are initially given the value undefined. The next part explains that the value that the var is assigned to is the value of the right hand side when the declaration is executed, not when the variable is created.
This applies to your situation because in your example, a is referenced like it is in the example. Using the information earlier, your code:
var a = a || "value";
Can be rewritten as:
var a;
a = a || "value";
Remember that all declarations are processed before any code is executed. The JavaScript engine sees that there's a declaration, variable a and declares it at the top of the current function or global scope. It is then given the value undefined. Since undefined is falsey, a is assigned to value.
In contrast, your second example throws a ReferenceError:
var a = k || "value";
It can also be rewritten as:
var a;
a = k || "value";
Now you see the problem. Since k is never a declared anywhere, no variable with that identifier exists. That means, unlike with a in the first example, k is never declared and throws the ReferenceError because it is referenced before declaration.
But how do you explain var a = "123"; var a = a || "124"; // a = "123"?
From the ES2015 Language Specification again:
Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collective define only one variable.
To put it plainly: variables can be defined in the same function or global scope more than once, but always define the same variable. Thus, in your example:
var a = "123";
var a = a || "124";
It can be rewritten as:
var a;
a = "123";
a = a || "124";
Declaring a in the same function or global scope again collectively only declares it once. a is assigned to "123", then it is assigned to "123" again because "123" is truthy.
As of ES2015, you should, in my opinion, no longer use var. It has function scoping and can cause unexpected assignments like those mentioned in the question. Instead, if you still want mutability, try using let:
let a = a || "value";
This will throw a ReferenceError. Even though all variables are hoisted, no matter which declarator you use (var, let, or const), with let and const it is invalid to reference an uninitialized variable. Also, let and const have block scope, not function scope. This is more clear and normal regarding other languages:
function foo() {
{
var a = 3;
}
console.log(a); //logs 3
}
Versus:
function foo() {
{
let a = 3;
}
console.log(a); //ReferenceError
}
var a = k || 'value';
var a = a || 'value';
a is declared when you call var a but k is not, that why first line is ReferenceError and second line is 'value'
if (a) console.log(1); // ReferenceError
in this case i think there is no doubt as a is not declared anywhere before using it so refrence error
var a = k || "value"; //ReferenceError
same case here k is not declared and we are trying to use it
var a = a || "value"; //"value"
now in this case when we are trying use a (a in r.h.s) that time a is already declared before using it and hence no error
Adding to this if you want to know difference between reference error and undefined
refrence error -indicate variable is not declared yet whereas
undefined - it is special value in javascript assigned to any variable as soon as it is declared undefined indicates that variable is declared but has not taken any value

Access variable in if block

var func = function () {
var i: number = 0;
if (i == 0) {
var y: number = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
func(); // output: 1
As you can see, I've declared variable y inside if block. So, I think it couldn't be referenced outside the scope.
But, when I've tried to run the code, the output is 1.
Is it an issue in typescript/javascript?
Variables in Javascript are hoisted, meaning that var y is moved to the top of the function as if it were declared there.
Initialization, however is not hoisted, so if you change i to be something other than 0, the variable y will still exist, but it will have the value undefined.
This means that the function is exactly equivalent to:
var func = function () {
var i: number = 0;
var y: number;
if (i == 0) {
y = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
To get the behavior you expect, you need to use let, which is a part of ECMAScript 2015 (ES6). It is block scoped, as you expect. It will also effectively work so that it is accessible only from the point of definition onwards, which is also probably as you would expect.
If you re-declare a JavaScript variable, it will not lose its value.
The second reference might pave way for a new variable syntax. Actually if you recall variable declaration is not neccessary in javascript. Simpe
y=1;
also works.
The second time when you reference y, outside if block, in my opinion, it tries a re-declaration and retains the old value.
Reference - http://www.w3schools.com/js/js_variables.asp
& http://www.w3schools.com/js/js_scope.asp
Javascript has function scope afaik. Any variable declared within a function, should be accessible from anywhere within the function. So, if you have a function checking if i==0, then you can achieve what you are trying to achieve.
This is as it is supposed to be. Javascript scopes by function, not by block. (This does make it unlike many popular languages)
Variables declared like var myVar = 10; will seep into nested functions but not the other way around. Variables declared like myVar = 10; will go global.
I couldn't find anything which suggested that typescript was any different.
Variables declared inside of an if statement are not scoped to the if statement. They're scoped to the current execution context. There's the Global execution context and then when a function is run, it creates it's own execution context. Inside of your function, you created the variables y and i. It doesn't matter that y was created inside of the if statement, because once it runs, y is created in the scope of the function. So then you do y.toString(), which can access y because it's scoped to the function not the if statement. That's why you get the output of 1. This is not an error, it's by design.

scope and availability of a variable inside a function

I am learning javascript and my question might not be used practically anywhere but for interview purpose I want to understand this clearly. below is the code. the first alert alerts 'undefined' and the second one is '4'. the second one is understandable. I want to know why the first alert doesn't alert '5' and undefined? whats the concept behind the same?
Thanks.
var x = 5;
function check(){
alert(x);
var x = 4;
alert(x);
}
check();
var is always hoisted (moved) to the begining of the scope. Your code is equivalent to this:
var x = 5;
function check(){
var x;
alert(x);
x = 4;
alert(x);
}
check();
As you can clearly see, the local x hides the global x, even if the local one doesn't have a value yet (so the first alert shows undefined).
A little extra: conditionals or other blocks don't influence hoisting the var declaration.
var x = 1;
(function() {
x = 3;
if (false) {
var x = 2; // var will be moved to the begining of the function
}
})()
console.log(x) // 1
JavaScript has some concepts that take a little getting used to for sure and sometime the answer for what seems to be a simple question is long winded but i will try to be as quick and concise as possible.
In JavaScript, variables are scoped by functions and you have two places that a variable can be scoped(have context) either 'globally' or 'locally'. The global context in a web browser is the 'window' so in your code example var x = 5; is a global variable. The local context in your code is within your check function so var x = 4; is a local variable to the check function. If you had more functions those would have their own function scope(context) and so on. As a word of caution if you forget the word "var" when declaring a variable inside a function it will become a 'global' variable, be careful. Now that we have the 'global', 'local' scope down what happens before a Javascript programs runs?
A few things happen in the Javascript Engine before your code is executed but we will only look at what happens to variables. Variables are said to be 'hoisted' to the top of their functional context or scope. What that means is that the variable declaration is 'hoisted or moved to the top and assigned the value 'undefined' but not initialized the initialization remains in the same spot they were. This hoisting also makes the variable available anywhere within the function it was defined.
So it looks something like this:
//to be accurate this is hoisted to its functional scope as well
var x;
x = 5;
function check() {
//declaration of 'x' hoisted to the top of functional scope,
//and assigned undefined
var x;
//this will alert undefined
alert(x);
//initialization of variable remains here
x = 4;
//so after the initialization of 'x' this will alert 4
alert(x);
}
check();
Happy Coding!!!
As an aside: They have a new feature named 'let' that you can implement to add block-level scoping in I believe EMCAScript 6 but is not implemented in all the browsers as of yet so that is why i placed it in an aside.

Categories

Resources