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 program prog.
  • If result.type is normal and its completion value is a value V, then return the value V.
  • If result.type is normal and its completion value is empty, then return the value undefined.

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 value V, then return the value V.

If result.type is normal and its completion value is empty, then return the value undefined.

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)