Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does block assigned value change global variable? [duplicate]

Tags:

javascript

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?

like image 421
ZenHeart Avatar asked Apr 13 '20 15:04

ZenHeart


People also ask

What are two reasons why you should not use global variables?

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.

Can function change global variables?

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).

Why are global variables hated?

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.

Why are global variables always initialized to zero?

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.


1 Answers

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:

  1. Inner function a is hoisted with let-like block scope inside the block ("(let) a")
  2. At the same time, a variable, also with name a, but with var semantics (ie. function scope), is created in the scope containing the block ("(var) a")
  3. When the line declaring the inner function is reached, the current value of (let) a is copied into (var) a (!!)
  4. 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 to a are to the block-level binding. Only the implicit B.3.3 assignment should go to the outer a

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.

like image 58
Ben Aston Avatar answered Sep 29 '22 09:09

Ben Aston