29

I was reading about JavaScript scopes and Hoisting. I saw a sample as below that made some doubts on my mind. So, I was wondering how it works.

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);

The code will alert 1 ! But if we eliminate the "function a(){}" part, the code will alert 10.

So, how does it works! What the "function a(){}" is doing here and how it affects the Scopes.

I also Can't understand the meaning of an empty "return;" in this code.

So, how this code works relying the JavaScript Scopes?

Amir Jalilifard
  • 2,027
  • 5
  • 26
  • 38
  • 4
    Read up about "hoisting". – elclanrs Jan 21 '15 at 05:16
  • 8
    This is called "hoisting" and here is an article that explains it and uses that exact code! http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html – Dom Jan 21 '15 at 05:17
  • Yes. I have read about hoisting but I still have doubt. Based on hoisting Function declarations and variable declarations are always moved (“hoisted”) invisibly to the top of their containing scope by the JavaScript interpreter. So, I still can't understand how it affects this code. – Amir Jalilifard Jan 21 '15 at 05:19
  • Yes. my doubt is exactly raised from the link you sent! – Amir Jalilifard Jan 21 '15 at 05:20
  • @GrijeshChauhan this question deletes line 5. The linked question doesn't consider that variation. – dcorking Jan 21 '15 at 13:14
  • @dcorking you are correct, my mistake - it is not exactly same. removed vote to close with [JavaScript 'hoisting'](http://stackoverflow.com/questions/15311158/javascript-hoisting). – Grijesh Chauhan Jan 21 '15 at 13:18

3 Answers3

38

First up, an "empty" return; statement simply exits the function at that point, returning undefined. It is equivalent to return undefined;.

The simple case, if you eliminate the function a(){} part, is that the b() function changes the global variable a to be 10, so then when you alert the value of a after running the b() function it is 10. Without that inner function, all references to a mean the global variable.

But with the function a(){} part, that function is declared inside b(). It is local to b(). So then you have two different as: the global variable, and the local one in b(). Regardless of where within the containing function another function statement appears, it is treated by the JS compiler as if it is at the top of the function. So even though the function a(){} line is at the end of the containing b() function in effect what happens when the code runs is the following:

var a = 1;              // declare a global variable a
function b() {
    function a() {}     // declare a local function a
    a = 10;             // update local a to be 10 instead of a function
    return;
}
b();
alert(a);  // show value of global a, which is still 1
nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • 2
    Another way of expressing line 3, which might better illustrate what happens is this: `var a = function a() {};` (in this case, either way is the exact same thing) – RickN Jan 21 '15 at 10:38
  • Is there an explicit reason why JS interpreters/compilers do this? – ASA Jan 21 '15 at 10:55
  • 1
    @Traubenfuchs - because that's what the language specification says to do. (The compiler doesn't really move variable declarations up, it compiles the function including declarations, and then it runs the function, but conceptually the "hoisting"/moving idea has the same result and most people seem to find it easier to visualise.) – nnnnnn Jan 21 '15 at 11:40
  • Thanks for your answer. This answer was the one that helped me to comprehend the code functionality.. – Amir Jalilifard Jan 21 '15 at 16:26
  • @Ajedi32 - No, function *statements* are "hoisted" *including* the definition. So inserting a `console.log(a)` as the first line of `b()` *will* log the function and `a` will *not* be `undefined`. (The values of variables declared with `var` are not hoisted, so if you had `var a = function...` *then* `a` would be `undefined` if logged before that line, but function *statements* are a special case.) – nnnnnn Jan 22 '15 at 01:10
  • @nnnnnn Actually it looks like you're right. That's strange, because I tried it in the console before I posted that comment and I could have sworn it printed undefined. (And yes, I did use `function a() {}`, not `var a = function(){}`) Trying it again though now, I can't seem to reproduce that behavior. – Ajedi32 Jan 22 '15 at 14:53
  • @Ajedi32 - If you ran the whole thing in the console it may have printed `undefined` as well, because `b()` returns `undefined`, as does the alert. – nnnnnn Jan 22 '15 at 21:26
  • @nnnnnn True, it's possible I accidentally skipped over what it printed and only looked at the return value instead. – Ajedi32 Jan 22 '15 at 21:28
  • Just to give a reference to understand it better: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting#function_hoisting – Satwik Gupta Nov 26 '21 at 09:06
16

In addition to nnnnnn's great answer, I tried to visualize the situation.

With function a(){}, your code behaves like this:

scope: window                         scope: b
      |                                   |
      | var a = 1;  //window.a = 1;       |
      |                                   |
      | function b() { -----------------> |
      |                                   | function a(){} // b.a(){} (hoisted to top)
      |                                   | a = 10;        // b.a = 10;
      |                                   | return;
      | } <------------------------------ |
      |                                   |
      | b();                              |
      | alert(a);  // alert(window.a);    |

We can see that function a(){} is hoisted to the top of the function because it includes a declaration. And if we remove function a(){}, the code will behave as follows:

scope: window                         scope: b
      |                                   |
      | var a = 1;  //window.a = 1;       |
      |                                   |
      | function b() { -----------------> |
      |                                   | a = 10;        // window.a = 10;
      |                                   | return;
      | } <------------------------------ |
      |                                   |
      | b();                              |
      | alert(a);  // alert(window.a);    |
Community
  • 1
  • 1
Memet Olsen
  • 4,578
  • 5
  • 40
  • 50
7

Your code is functionally the same to this code:

var a = 1;
function b() {
    var a = function() {}
    a = 10;
    return;
}
b();
alert(a);

Using the function NAME() { ... } notation effectively puts that function declaration at the beginning of the current scope as a local (to that scope) declaration.

in fact if you execute

var a = 1;
var c= 2;
function b() {
    a()
    a = 10;
    return;
    function a() { alert(c) }
}
b();
alert(a);

It will output:

2
1

Personally I don't use this kind of notation, I always explicitly use assignments.

jsfiddle

Hoffmann
  • 14,369
  • 16
  • 76
  • 91