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)