I am getting a very strange output on the below scenarios:
function test(){
var test=123;
console.log(test)
}
// this output : 123
(function test(){
What you're seeing isn't related to hoisting.
Your first example is quite straightforward:
(function test(){
var test=123;
console.log(test)
})()
You're creating a variable (via var
) called test
within the scope of the function, and assigning it a value, then outputting that value.
Your second example leaves out var
:
(function test() {
test = 123;
console.log(test);
})();
...and so test
has quite a different meaning: Within a function created by a named function expression, the function's name is an identifier resolving to the function. So test
in that code is an identifier for the function.
That identifier is read-only when you've used a function expression, so your test = 123;
line is ignored, and the console.log
line outputs a representation of the function (as though test = 123;
weren't there at all).
I'd forgotten that identifier is read-only (when created by an expression), but it is: From the specification:
FunctionExpression : functionBindingIdentifier ( FormalParameters ) {FunctionBody}
- If the function code for FunctionExpression is strict mode code, let strict be true. Otherwise let strict be false.
- Let scope be the running execution context's LexicalEnvironment.
- Let funcEnv be NewDeclarativeEnvironment(scope).
- Let envRec be funcEnv's EnvironmentRecord.
- Let name be StringValue of BindingIdentifier.
- Perform envRec.CreateImmutableBinding(name, false).
- ...
Note Step 6: The binding creating the identifier is immutable (can't be changed).
Note that this isn't true for the identifier (binding) created by a function declaration, which is mutable; but function expressions and function declarations treat the identifiers created by the function name completely differently. (For instance: A function declaration puts the name in scope where the declaration is, but a function expression doesn't.)