3

It all started with these simple lines of code:

a = 3;
b = 2;

function line(x) {
    var a = 5;
    var b = 4;

    return a*x+b;
}

// returns 17
b = line(a) - b;
alert(b);

// returns 36
c = line(a) + b;
alert(c);

Both alerts return 17 and 36, respectively.
The control works as expected. Until…


I make the mistake of changing
the inside of the function like so:

function line(x) {
    var a = 5; 
        b = 4;

    return a*x+b;
}

Suddenly line 13 returns 15, line 17 returns 23
and the situation continues to deteriorate as I follow var
down the rabbit hole, becoming more enmeshed as I make my descent.

I realize I could make a mental note to always use var
and rest knowing my code will always work as intended
but this has become a matter of principle now and now I need
to understand how var actually works.

Here are links to the four hells,
made by (and possibly for) me:

Riddle #1 http://jsfiddle.net/js_test/gNEmY/

Riddle #2 http://jsfiddle.net/js_test/FJVYL/

Riddle #3 http://jsfiddle.net/js_test/Vz7Sd/

Riddle #4 http://jsfiddle.net/js_test/RaA5J/

If anyone could give me insight
into what's happening under the hood
to wit, what happens during each alert() call;
I would really appreciate it.

Wilhelm
  • 1,407
  • 5
  • 16
  • 32
  • possible duplicate of [Difference between using var and not using var in JavaScript](http://stackoverflow.com/questions/1470488/difference-between-using-var-and-not-using-var-in-javascript) – Bergi Jan 13 '14 at 02:35
  • Thanks @Bergi I understand that much. I'm more looking for insight into what's going on in the one of the riddles. – Wilhelm Jan 13 '14 at 02:41
  • Varaible [Declaration Binding Instantiation](http://ecma-international.org/ecma-262/5.1/#sec-10.5) is described in ECMA-262. The *VariableEnvironment* can be thought of as an inaccessible object whose properties are the local variables. – RobG Jan 13 '14 at 03:48

4 Answers4

2

var creates a local variable, scoped to the function in which it appears. Any variable that is declared in the global scope becomes a property of the global object (in a browser, window), and any variable referenced in a function which is not declared with var in that function refers to the surrounding scope, quite possibly the global scope.

There is also a new keyword, let, which is coming in the upcoming version of ECMAScript and is already in some browsers, which creates a block-scoped variable.

So, in your first example (I am going to assume this is running in a browser), you create window.a and window.b which are bound to 3 and 2, respectively. Function line declares local variables a and b. When window.a is passed to line, the parameter x is bound to the value 3 (the value of window.a). Local variables a and b are 5 and 4, respectively. These have nothing to do with window.a and window.b. The calculation a*x+b is, thus, 5*3+4, or 19.

The code b = line(a) - b passes window.a to line, calculates the value 19, subtracts the current value of window.b from it (2), resulting in 17, which is then assigned to window.b. The value of 36 comes from line(3) (which is still 19) plus 17 (the new value of window.b).

When you removed the var from the assignment to b in function line, you changed it so that b is no longer a local variable. In that case, all references to b in the function refer to the global value window.b.

In the case of your Riddle #2, where you got 23, after the first call window.b is equal to 15. When line(a) is called a second time, window.b is set back to 4, the a*x+b calculation still gets 19, and then window.b (4) is added again, making 23.

It is important to note that the var keyword declares a variable that is function-scoped, not block-scoped as you might expect from other C-derived languages. For example, in this function:

function scope(array) {
    var a = 7;

    for (var b = 0; b < array.length; ++b) {
        var c = array[b];
    }

    alert(a + b + c);
}

All the variables have scope which extends over the entire function. In particular, the scope of c is not limited to the for loop.

A variable that is not declared in a particular scope doesn't necessarily reference a global variable, however. If it is nested inside another function scope, it will refer to that nested scope. Consider the following example:

var b = 7;

function outer() {
    var b = 42;

    function inner() {
        return b; // refers to b in outer, not global
    }

    return inner();
}

alert(outer()); // 42, not 7
David Conrad
  • 15,432
  • 2
  • 42
  • 54
  • That's a better way to phrase it. I only meant that b is not declared in `inner`, not that it is not declared at all. – David Conrad Jan 13 '14 at 17:18
0

The variable declared without var is global variable, this is the rule. But remember if you want to declare multiple local variable with one var, separate them with comma.

var a = 5; // local variable
    b = 4; // global varialbe

var a = 5, // local variable
    b = 4; // local varialbe
xdazz
  • 158,678
  • 38
  • 247
  • 274
  • "The variable declared without `var`" is a non sequitor. A variable can **only** be declared using `var` (though including an identifier in the formal parameters of a function declaration is effectively a variable declaration). – RobG Jan 13 '14 at 03:35
0

If you're in the global scope then there's no difference.

If you're in a function then "var" will create a local variable, "no var" will look up the scope chain until it finds the variable or hits the global scope (at which point it will create it).

Source

In the case of your first function, line(a) is processed first, and by the time it runs, you've set b = 4. Therefore, and because b is a globally defined variable, when it runs line(a) - b, you're getting:

line(a) (returns 19)

And minus b (b = 4)

19 - 4 = 15

You can simplify your statement and get the same result:

//define global variable b
var b = 2;
function line(x) {
    //x = 3
    //update global variable b; it now = 4
    b = 4;
    //forget the math and just return 19
    return 19;
}

//run function; before, b = 2, after b = 4
b = line(3) - b;
alert(b);

Likewise with your second example, because your function changes the value of global variable b, the only difference is that you're adding 4 instead of subtracting (19 + 4 = 23).

To avoid this confusing nature of variables, always be careful to differentiate between global and local variables whenever possible, and only drop var when you need to explicitly update global variables in functions. In actual fact too, since global variable always remain in memory, you should use globals only when you explicitly need them. Here's an unambiguous example:

var globalVar;
function someFunc(x) {
    var tempVar = 4;
    return x * tempVar;
}
Community
  • 1
  • 1
brandonscript
  • 68,675
  • 32
  • 163
  • 220
  • Thanks @r3mus I understand that much. Would you mind checking out [Riddle #2](http://jsfiddle.net/js_test/FJVYL/) I'm still confused what happens when I call `alert(a);` after `c = line(a) + b;` – Wilhelm Jan 13 '14 at 02:46
  • Again, you've updated `a` so that it now = 4 (you did this when you called `line(a)` the first time. When you run it a second time, the value of `a` is different (= 5). – brandonscript Jan 13 '14 at 02:47
  • I understand how a `local` can update a `global` variable when `var` is omitted. However, with regards to [Riddle #2](http://jsfiddle.net/js_test/FJVYL/), I'm confused how `c = line(a) + b` can `return` 23 if `b` becomes `b = line(a) - b` before the second `alert` call? Why would `c = line(a) + b` use the `local` twice (e.g. 5*3+4+4) instead of using the local variable and updated `global` (e.g. 5*3+4 + `b = line(a) - b;`)? – Wilhelm Jan 13 '14 at 09:06
  • `var` only creates a local variable if you're in a function; in this case, because you're not for b, when you run through the function again, you're changing the value of `b` back to 4. it's just the same as `19 - 4 = 15`, but since you're adding `b` this time, it's `19 + 5 = 23`. – brandonscript Jan 13 '14 at 16:10
0

VAR declares a new instance in memory for a new variable. Simply saying b = 4 will try to change the value of b to 4. If b has not been initialized nothing will happen.

This ties into the concept of PRIVATE and LOCAL variables, and naming conventions.

Firstly, you have 2 variables called a and 2 called b. This is bad practice because you might change the value of the other one by mistake, or its old value could still be sitting in memory because it was already initialized and given a value. Remember, you do not always NEED to assign a value when initializing, it's just best practice.

Secondly, your function can modify a variable from the level above it. In other words, by taking away VAR on that one line it's possible to modify the original value of b which you set to 2 at the beginning of the file.

TLDR; Don't use the same variable name more than once, and always use your VAR when trying to make a NEW variable.

Alexander
  • 612
  • 1
  • 9
  • 20
  • 1
    It isn't "depending on the rules of your compiler", but rather the rules of Javascript. If you have nested functions, and the outer function declares a variable and the inner function references it without declaring it, it *will* access the one "from the level above it". – David Conrad Jan 13 '14 at 02:52