Javascript scoping variables theory

2019-02-19 10:59发布

问题:

I have two example pages that are behaving differently and I would like to know why. To me they seem consistent with each other based one what I have gathered about scoping in javascript.

1.html:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
  function demofunction(x, y) {
    z=x+y;
  }
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>4-6.htm</title>
</head>
<body>
    <h1>Bad Scoping</h1>
    <script type="text/javascript">
    //<![CDATA[
    demofunction(3, 2);
    alert(z);
    var z;
    alert(z);
    //]]>
    </script>
    <p>&nbsp;</p>
</body>

</html>

In this example, demofunction runs and sets the global variable z to 5. alert runs and since there is no z in scope it grabs the global scope and finds a z which is equal to 5, it alerts that. then a new local variable named z is defined. the second alert can see that local variable, but since it is undefined, it choses the global one and alerts 5 again.

2.html

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Bad Scoping</title>
  <script type="text/javascript">
  //<![CDATA[
  first = 6;
  document.writeln('<p>first is ' + first + "</p>");

  function letsSee() {
    alert(first);
    var first;
    first = 4;
  }
  letsSee();
  document.writeln('<p>but now first is ' + first + "</p>");

  //]]>
  </script>
</head>
<body>
</body>
</html>

global first gets set to 6. letsSee() runs and the alert (if consistent) should see no local variable named first so it should alert the global variable 6. then a local first is defined, which is then set to 4. letsSee() exists and the last print prints the global first and shows 6 again.

this does NOT happen though. what happens is that it shows 6, alert has undefined, and shows 6. my question is directed to why does it alert undefined rather than 6? if i comment the line inside letsSee for var first; i then see that it alerts 6 and then display's 4. this makes sense to me. but why does having that var first after the alert make a difference to what the alert call sees as the value? esp when it didn't make a difference in 1.html.

回答1:

var and function declarations are hoisted before any code is executed.

function letsSee() {
  alert(first);
  var first;
  first = 4;
}

behaves like

function letsSee() {
  var first = undefined;
  alert(first);
  first = 4;
}

As an easier to understand example, you can call functions before they are declared:

foo();  // works!
function foo() { ... }

This is because the function declaration is looked at first and hoisted, you can call it anywhere regardless of when/where in the code you declare it. The same happens with variables declared using var. The variable itself (the name) is hoisted. Writing var foo; anywhere within a given scope will make any higher-scoped variable foo unaccessible within that scope.

var foo = 'foo';

function () {
    alert(foo);        // All of these can't see the higher-scoped
    doSomething(foo);  // foo = 'foo' variable, because the
    return foo;        // local foo has already been hoisted.

    var foo;  // masks the higher-scoped foo within the entire scope,
              // even if it doesn't appear to be executed
}


回答2:

There are two things that are throwing you off

1. your assumption that the <script>...</script> block has its own scope like a function has scope. The truth is that all <script> blocks exist in the same global scope.

2. you've also assumed that the var declaration occurs in the order in which it appears in the code. The truth is that all vars in a given scope are executed at the beginning of that scope. (-edit- just to clarify: the var declaration happens at the beginning of the scope, but any assignment operation remains at its original spot in the code. The value of a variable that has been declared, but not yet had a value assigned is undefined).

So... in your first example, var z is actually declaring z in the global scope, then the call to demofunction() operates on that global z, and both alerts run against the same global z.

Whereas, in your second example, first is initially set in the global scope, then declared again within the function's local scope. In the function, the alert and the assignment both operate on the local variable. Outside the function the writeln()s operate on the global variable.



回答3:

I think that is because function declarations are hoisted in JavaScript - i.e. that function runs up the top, where that variable you are trying to access is not defined.