Why does JavaScript variable declaration at console results in "undefined" being printed?
I have already read the following SO posts:
Why does this JavaScript code print “undefined” on the console?
Why does Chrome & FireFox console print undefined?
Why does the JS console return an extra undefined?
But none of it explains why the JavaScript console prints undefined when I declare a variable as follows:
var a;
Solution 1:
It prints the result of this expression - which is undefined
. And yes, var a
is a valid expression on its own.
Actually, you should rather be amused by why console
prints undefined
when you write var a = 3
or something like this. It also prints undefined
if function anyFunctionName() {}
statement is processed. In fact, all the var
and function
declaration (!) statements seem to be ignored if there's another statement with some 'real' result:
>>> var a = 3;
undefined
>>> var a = 3; a = 4;
4
>>> var a = 3; a = 4; var a = 5; function f() {};
4 // !!!
Now, I suppose the real reason behind is behaviour of eval
statement, as described here:
- Let
result
be the result of evaluating the programprog
.- If
result.type
isnormal
and its completion value is avalue V
, then return thevalue V
.- If
result.type
isnormal
and its completion value isempty
, then return the valueundefined
.
So now the question is, what does var a = 4
statement return? Guess what: it's not 4.
The production VariableStatement : var VariableDeclarationList; is evaluated as follows:
- Evaluate VariableDeclarationList.
- Return (normal, empty, empty).
Now the most interesting part: what happened in the last example, why 4 is the result? That's explained in this section:
The production Program : SourceElements is evaluated as follows:
- Let result be the result of evaluating SourceElements.
[...]
The production SourceElements : SourceElements *SourceElement* is evaluated as follows:
- Let headResult be the result of evaluating SourceElements.
- If headResult is an abrupt completion, return headResult.
- Let tailResult be result of evaluating SourceElement.
- If tailResult.value is empty, let V = headResult.value, otherwise let V = > tailResult.value.
- Return (tailResult.type, V, tailResult.target)
Both function f() {}
and var a = 5
statements' return values were (normal, empty, empty)
. So the script ended up with giving out the result of the first statement (starting from the script's end, so technically it's the last one) that's not (normal, empty, empty)
. That is the result of a = 4
assignment statement - which is 4
.
P.S. And now for some icing on the cake: consider the following:
>>> function f() {}
undefined
>>> (function f() {})
function f() {}
The difference is quite subtle: the first input is treated as a Function Declaration
statement, which, according to this rule...
The production SourceElement : FunctionDeclaration is evaluated as follows:
- Return (normal, empty, empty).
... will eventually produce undefined
when eval
-ed, as we already know.
The second input, however, is treated as a Function Expression
, which is evaluated to the function itself. That means it'll be passed through eval
and eventually returned to the console (in its format).
Solution 2:
var a=1;
a
gives:
1
while
var a=1;
gives:
undefined
in the first case the console evaluates a so it prints the value of a
in the second case the console does not evaluate the value of a, but it evaluates the expression itself.
Solution 3:
because all you are doing is declaring there is a variable - what is it? a string, an integer, a boolean - we don't know yet - hence undefined
Solution 4:
Each time you evaluate a line of code, you get a completion type/record result
which has 3 attributes: type
, value
and target
. According to the Ecma specification:
If
result.type
is normal and its completion value is a valueV
, then return the valueV
.
If
result.type
is normal and its completion value isempty
, then return the valueundefined
.
It turns out that when you declare a variable or a function, the completion type
is (normal,empty,empty)
. Since the result.type
is normal
and value is empty
, it returns the value undefined
.
However when you type a = 3
, it's an assignment expression and its completion type is (normal, GetValue(), empty)
. So you will just see 3
in the console.
For terms around statement
and expression
, see difference statement/expression.
For different values of completion type, see completion type documentation.
If you check the completion type documentation, you can see that empty statement ;
has also a completion type (normal, empty, empty)
(so it should return undefined
), and indeed it's the case. For the same reason, if (x>3) {console.log(x)}
also returns undefined
and do {console.log(3);} while (false)
too.
However, (function f(){})
doesn't return undefined
because it's an expression statement.
Test by yourself. Here are some more examples:
eval('function f(){}'); // Return (normal, empty, empty), undefined
eval(';'); // Return (normal, empty, empty), undefined
eval('(function f(){})'); // (normal, GetValue(exprRef), empty), ExpresionStatement
function foo() {
return 4;
} // Return (normal, empty, empty), undefined
foo(); // (return, 4, empty), 4
eval('function foo() {return 5;}'); // Return (normal, empty, empty), undefined
eval('foo();'); // (return, 4, empty), 4
let x = 4; // (normal, empty, empty), undefined
if (x > 3) {
console.log(x);
} // (normal, empty, empty), undefined
console.log(6); // (normal, empty, empty), undefined
eval('let x = 4; if (x>3) {console.log(x)}'); // undefined
let y = 5; // (normal, empty, empty), undefined
do {
console.log(3);
y++;
} while (y < 8); // this returns y, can you explain why?
do {
console.log(3);
} while (false); // undefined since (normal, empty, empty)