var a = 0;
if (true) {
console.log(a)
a = 1;
function a() {}
a = 21
console.log(a)
}
console.log(a)
In my opinion, because function declaration hoisting, a = 1
and a = 21
will change local function variable ,so in block will output 21,and outside is 0,but the true result is outside output 1.
Debug with chrome, result like that
When running on function a() {}
,it will change local and global variable. so weird?
Who can give me some explanation?
Using global variables causes very tight coupling of code. Using global variables causes namespace pollution. This may lead to unnecessarily reassigning a global value. Testing in programs using global variables can be a huge pain as it is difficult to decouple them when testing.
Functions can access global variables and modify them. Modifying global variables in a function is considered poor programming practice. It is better to send a variable in as a parameter (or have it be returned in the 'return' statement).
The reason global variables are bad is that they enable functions to have hidden (non-obvious, surprising, hard to detect, hard to diagnose) side effects, leading to an increase in complexity, potentially leading to Spaghetti code.
Global and static variables are initialized to their default values because it is in the C or C++ standards and it is free to assign a value by zero at compile time. Both static and global variable behave same to the generated object code.
The observed behavior is peculiar to non-strict mode, and Firefox does the same thing.
The reason it is behaving this way, is that it is following the Web Compatibility Semantics, as described in Annex B 3.3 in the spec.
The details are very complicated, but here is what the authors of this part of the engine implemented:
When an inner function
a
exists in a block, in sloppy mode, and when the Web Compatibility Semantics apply (scenarios described in the spec), then:
- Inner function
a
is hoisted withlet
-like block scope inside the block ("(let) a
")- At the same time, a variable, also with name
a
, but withvar
semantics (ie. function scope), is created in the scope containing the block ("(var) a
")- When the line declaring the inner function is reached, the current value of
(let) a
is copied into(var) a
(!!)- Subsequent references to name
a
from inside the block will refer to(let) a
So, for the following:
1: var a = 0
2: if(true) {
3: a = 1
4: function a() {}
5: a = 2
6: }
7: console.log(a)
...this is what happens:
Line 1: (var) a
is added the the variable environment of the outer scope, and 0
is assigned to it
Line 2: An if block is created, (let) a
(ie. the function a() {}
) is hoisted to the top of the block, shadowing (var) a
Line 3: 1
is assigned to (let) a
(note, not (var) a
)
Line 4: The value of (let) a
is copied into (var) a
, so (var) a
becomes 1
Line 5: 2
is assigned to (let) a
(note, not (var) a
)
Line 7: (var) a
is printed to the console (so 1
is printed)
The web compatibility semantics is a collection of behaviors that try to encode a fallback semantic to enable modern browsers to maintain as much backwards compatibility with legacy code on the Web as possible. This means it encodes behaviors that were implemented outside of the spec, and independently by different vendors. In non-strict mode, strange behaviors are almost expected because of the history of browser vendors "going their own way."
Note, however, that the behavior defined in the specification might be the result of a miscommunication. Allen Wirfs-Brock said on Twitter:
In any case, the reported... result of a==1 at the end isn’t correct by any reasonable interpretation that I ever imagined.
...and:
It should be
function a(){}
! Because within the block, all explicit references toa
are to the block-level binding. Only the implicit B.3.3 assignment should go to the outera
Final note: Safari, gives a totally different result (0
is printed by the first console.log
, and 21
is printed by the final console.log
). As I understand it, this is because Safari simply does not yet encode the Web Compatibility Semantics (example).
Moral of the story? Use strict mode.
More, details, here and here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With