3

Slightly confused by this Eloquent Javascript explanation of parameters and scopes.

It states that variables declared outside of a function are global, that variables declared inside a function are local, and that variables declared inside a function without a preceding var are essentially referencing a similarly named global variable. Fine. That makes sense. But then this code throws me for a loop.

var x = "outside";

var f1 = function() {
  var x = "inside f1";
};
f1();
console.log(x);
// → outside

var f2 = function() {
  x = "inside f2";
};
f2();
console.log(x);
// → inside f2

Logging out the value of x in the first function should result in "inside f1" because that variable was declared locally. And that second function (being that it contains a variable declared without var and thus references the global one declared up at the very top) should result in "outside." But...it doesn't in either case.

I get the gist of what's supposed to happen. But unless I'm reading incorrectly, it seems as if it's the opposite of what the author describes. This can't be a typo.

jdphenix
  • 15,022
  • 3
  • 41
  • 74
calyxofheld
  • 1,538
  • 3
  • 24
  • 62

3 Answers3

3

The x in f1 is a new variable only accessible within f1 and has no effect on the first, global x. The example code in your question could essentially be written like the following for clarity:

    var globalX = "outside";
    var f1 = function() {
      var localF1X = "inside f1";
    };
    f1();
    console.log(globalX); // → outside

    var f2 = function() {
      globalX = "inside f2";
    };
    f2();
    console.log(globalX); // → inside f2
rgthree
  • 7,217
  • 17
  • 21
1

In JavaScript, variables are scoped at the function level (or global level if you are declaring variables outside of a function).

You can read more on JavaScript variables and "hoisting" here: http://javascriptissexy.com/javascript-variable-scope-and-hoisting-explained/

Therefore:

var x = 'a';
function f1() {
    var x = 1;
    console.log(x);
}

f1(); //outputs 1
console.log(x); //outputs 'a'

function f2() {
    x = 'b';
}

console.log(x); //still outputs 'a'

f2();

console.log(x); //now outputs 'b'
Norman Breau
  • 2,132
  • 16
  • 35
1

Variables declared inside functions are only accessible (or scoped) from inside those functions. The sample may more clear if it were like this:

function f1() {
  var x = "Inside f1"; 
}

console.log(x);

will result in

ReferenceError: x is not defined

However, functions that have a variable that is declared without var is an implicit global (and either bad practice or a missed error):

function f2() {
  y = "Inside f2"; 
}

console.log(y);

Will work as you expect, while also declaring an implicit global.

It is worth mentioning "use strict";, which runs the code in ES5's Strict Mode. You generally want to declare this inside a function, which causes the function to be run in strict mode, and avoids the semantics of strict mode from breaking interoptability with other code.

function f3() {
  "use strict";
  z = "Inside f3"; 
}

console.log(z);

will result in

ReferenceError: z is not defined

Because strict mode doesn't allow you to declare an implicit global.


To clarify based upon your comments, implicit globals will "overwrite" each other. More plainly using JavaScript terminology:

  • x = 10 will declare a property on the environment's global object x, either window.x for the browser environment and global.x for the Node/IO environment.
  • x = 20 will redefine the same property discussed above.

Here's a little snippet you can run in any environment that will demonstrate this. I am by no means stating you should use implicit globals, but rather providing another example as to why you shouldn't.

function functionThatNeedsGreaterThan50(value) {
  // Skip checking the parameter because we trust the 
  // other developers on the team to make sure they call
  // this right. 
}

function f4() {
  q = 42; 
}

function f5() {
  q = 62; 
}

f4();
f5();

console.log(q);

// sometime thousands of calls later, one of which was
f4(); 

// I thought this was 62 but

functionThatNeedsGreaterThan50(q); 
Community
  • 1
  • 1
jdphenix
  • 15,022
  • 3
  • 41
  • 74
  • I know I accepted this as an answer last night, but...what's wrong with declaring an implicit global? I imagine that doing so makes it harder to find the source of any potential bugs arising from the result of doing so. Being that the global variable is not defined up top like the other *normal* global variables. Right? – calyxofheld May 02 '15 at 22:12
  • 1
    @sabaeus I added by own thoughts as well. – jdphenix May 02 '15 at 22:26
  • So basically, in order to use the *correct* implicit global, you must first call the function in which it was created? – calyxofheld May 02 '15 at 23:03
  • 1
    Sort of - implicit globals (or rather **any** global) is subject to mutation. If you just wanted to make sure you're using the correct value, it's better to scope it appropriately inside an object or closure. – jdphenix May 02 '15 at 23:05