4

Hi I am trying to understand the JavaScript fundamentals, and stuck in one condition.

    var foo = 1;
    function bar(){
        foo = 10;       
        return;
        function foo(){}
    }
    bar();
    alert(foo);

Here alert(foo), will give me 1, and I know after return statement, function foo() will not execute. But now if change the code:

    var foo = 1;
    function bar(){
        foo = 10;       
        return;
    }
    bar();
    alert(foo); 

In bar function, If I will remove function foo(). then alert(foo) will give me 10

Please help, if someone can explain me why?

Jack
  • 804
  • 7
  • 18
pradeepks
  • 53
  • 1
  • 6

6 Answers6

11

This is called Javascript hoisting

I will try to explain it in details.. This is what we have

var foo = 1;

function bar(){
  foo = 10;       
  return;
  function foo(){}
}

bar();
alert(foo);

The interpreter will rewrite this as

var foo = 1;

function bar(){
  function foo(){}  // this is also equal to var foo = function(){};
  foo = 10;       
  return;      
}

bar();
alert(foo);

So now explaining you the hoisted code.

var foo = 1; // global variable; 

function bar(){
  var foo = function(){};  // foo is local variable of type function
  foo = 10;                // foo is changed to hold a number
  return;      
}

bar();
alert(foo);  // you alert global variable.

As you can see if the code function foo(){} is present it is treated as a local variable within the bar() scope and any change to the foo is treated as a local variable change..

  • When you have function foo(){} in your bar() you are not even touching the global variable.. hence alerts 1.

  • When you don't have function foo(){} you are touching the global variable and hence alerts 10.

    Now I hope you understand the output..

Rajshekar Reddy
  • 18,647
  • 3
  • 40
  • 59
  • 2
    It worth mentioning here that function expressions like `foo = function(){};` are not hoisted onto the beginning of the scope, therefore they cannot be used before they appear in the code, meaning if you do `function bar(){ foo = 10; return; foo = function(){} }` foo will return 10 – phobia82 Dec 07 '16 at 11:12
  • 1
    nice explanation upvoted :) but how do you know interpreter will convert this code into another code ?? – Mahi Dec 07 '16 at 11:24
  • @Mahi because that is how javascript interpretors work. It is called "Hoisting" and it is a common concept. Look it up. – Dellirium Dec 07 '16 at 11:26
  • @Dellirium javascript interpretors is javascript engine ?? – Mahi Dec 07 '16 at 11:28
  • @phobia82 that is a good point. – Rajshekar Reddy Dec 07 '16 at 11:30
  • @phobia82 how can you say that first it will move to top using function hoisting . what if it first converted to `var foo = function(){}; ` then it cannot be go on top because var foo is not hoisted . right ? – Mahi Dec 07 '16 at 11:40
  • @Mahi yes if you declare it as a variable then it is not hoisted. – Rajshekar Reddy Dec 07 '16 at 11:43
  • @Reddy then you can't say this line `function foo(){} // this is also equal to var foo = function(){};` because if it was equal then it won't move to top . – Mahi Dec 07 '16 at 11:45
  • @Mahi please note that using `var` you are explicitly restricting the variable to the function scope. If you remove the `foo` function completely, and use `var foo = 10`, the the alert will be `1` because `var foo = 10` is a different variable that the outside `foo` – phobia82 Dec 07 '16 at 11:52
  • @Reddy `function a() {} ` is equal to `var a=function a() {} ` or `var a=function () {} ` or `a=function a() {} ` or `a=function () {} ` ? – Mahi Dec 07 '16 at 11:59
  • @Mahi `function a() {}` after hoisting is interpreted as `var a=function () {}` – Rajshekar Reddy Dec 07 '16 at 12:03
  • @Reddy thanks last question . https://jsfiddle.net/xhbng622/4/ will 5th line execute ? since it it below return and it is not hoisted . – Mahi Dec 07 '16 at 12:22
  • @Mahi thank you for that, I was wrong in my comments, the above fiddle you provided will be interpreted as https://jsfiddle.net/RajReddy/xhbng622/5/ a good read about this hoisting http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html – Rajshekar Reddy Dec 07 '16 at 12:27
  • @Reddy Lol how ? :-( ok i will read that . but why interpreted convert code . – Mahi Dec 07 '16 at 12:32
  • @Mahi did you go through that link about the hoisting? – Rajshekar Reddy Dec 07 '16 at 12:34
  • `Function declarations and variable declarations are always moved (“hoisted”) invisibly to the top of their containing scope by the JavaScript interpreter.` i just mentioned functions, But variables **declaration** too are hoisted.. – Rajshekar Reddy Dec 07 '16 at 12:35
  • @Mahi I added more explanation as another answer to the same thread.. http://stackoverflow.com/a/41018365/2592042 – Rajshekar Reddy Dec 07 '16 at 13:09
5

I know after return statement ,function foo() will not execute.

That's not true.

Function declarations are hoisted.

function foo(){} creates a local variable called foo (assigning the new function to it) and then foo = 10 overwrites it. You never test the value of that foo variable though.

In bar function , If I will remove function foo(). then alert(foo) will give me 10

You no longer have a local variable called foo so you are overwriting the global variable with the same name.


Compare:

(function() {
  console.log("Version 1");
  var foo = 1;

  function bar() {
    console.log("At top of bar, foo is " + foo);
    foo = 10;
    console.log("After assignment in bar, foo is " + foo);
    return;

    function foo() {}
  }
  bar();
  console.log("Global foo is " + foo);
}());

(function() {
  console.log("Version 2");
  var foo = 1;

  function bar() {
    console.log("At top of bar, foo is " + foo);
    foo = 10;
    console.log("After assignment in bar, foo is " + foo);
    return;
  }
  bar();
  console.log("Global foo is " + foo);
}());
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • "That's not true" is not the correct answer here. Indeed alert(foo) does give him 1, and as he stated, function foo will not execute... the statement is 100% correct. Your answer is obviously correct, but, please clear out the wording. – Dellirium Dec 07 '16 at 10:48
  • @Dellirium — The function declaration **will execute**, it will create a function. That function is never called, but the statement that creates it will still execute. – Quentin Dec 07 '16 at 10:50
  • This is correct, to help clear this up for OP, before you set `foo = 10` write `console.log(foo)` it should be set to the foo function you declared after the return. – George Dec 07 '16 at 10:51
  • 1
    I've made a fiddle for further clarification https://jsfiddle.net/2rzvu18q/1/ – George Dec 07 '16 at 10:58
4

When you write this function :

function bar(){
    foo = 10;       
    return;
    function foo(){}
}

The javascript read this :

function bar(){
    function foo(){}
    foo = 10;       
    return;
}

The function foo is created into your local function bar. And when you write foo = 10,You overwrite the function foo in the local scope and not the global variable.

So your alert give you 1 because you never update the global variabe.

R3tep
  • 12,512
  • 10
  • 48
  • 75
3

The problems here are hoisting and closure .

The declaration function foo(){} is hoisted, meaning in this case, even though it is written at the end of the function, it will be available everywhere within the scope, including before it's definition.

if function foo(){} IS NOT present, the statement foo = 10; overwrites the foo defined in the global scope. Therefore the global foo === 10.

If function foo(){} IS present, the statement foo = 10; just overwrites the function foo in the local scope, the global foo won't get touched hence global foo === 1

var foo = 1;
function bar(){
   console.log(typeof foo) // function      
   return;
   function foo() {}
}
bar();
alert(foo); 

Opposed to:

var foo = 1;
function bar(){
   console.log(typeof foo) // number      
   return;
   // function foo() {}
}
bar();
alert(foo); 
MoeSattler
  • 6,684
  • 6
  • 24
  • 44
2

So basically what is happening is as if you have declared var foo = 10 because function declaration in javascript are hoisted up top complier sees your code as follows .

var foo = 1;
function bar(){
  var foo;
  foo = 10;       
  return;
  function foo(){}
}
bar();
alert(foo);

so in fact foo = 10 never overwrites the global foo; it is kept local to the function . so alert will get passed the global one .

Ahmed Eid
  • 4,414
  • 3
  • 28
  • 34
1

In addition to my previous answer in the same thread I am adding another answer to put in more details about the Hoisting feature in JavaScript as the previous answer is already accepted by the OP for its content.

First lets get comfortable with what scoping is

Scoping in JavaScript

One of the sources of most confusion for JavaScript beginners is scoping. Actually, it’s not just beginners. I’ve met a lot of experienced JavaScript programmers who don’t fully understand scoping. The reason scoping is so confusing in JavaScript is because it looks like a C-family language. Consider the following C program:

#include <stdio.h>
int main() {
    int x = 1;
    printf("%d, ", x); // 1
    if (1) {
        int x = 2;
        printf("%d, ", x); // 2
    }
    printf("%d\n", x); // 1
}

The output from this program will be 1, 2, 1. This is because C, and the rest of the C family, has block-level scope. When control enters a block, such as the if statement, new variables can be declared within that scope, without affecting the outer scope. This is not the case in JavaScript. Try the following in Firebug:

var x = 1;
console.log(x); // 1
if (true) {
    var x = 2;
    console.log(x); // 2
}
console.log(x); // 2

In this case, Firebug will show 1, 2, 2. This is because JavaScript has function-level scope. This is radically different from the C family. Blocks, such as if statements, do not create a new scope. Only functions create a new scope.

To a lot of programmers who are used to languages like C, C++, C#, or Java, this is unexpected and unwelcome. Luckily, because of the flexibility of JavaScript functions, there is a workaround. If you must create temporary scopes within a function, do the following:

function foo() {
    var x = 1;
    if (x) {
        (function () {
            var x = 2;
            // some other code
        }());
    }
    // x is still 1.
}

This method is actually quite flexible, and can be used anywhere you need a temporary scope, not just within block statements. However, I strongly recommend that you take the time to really understand and appreciate JavaScript scoping. It’s quite powerful, and one of my favorite features of the language. If you understand scoping, hoisting will make a lot more sense to you.


Declarations, Names, and Hoisting

In JavaScript, a name enters a scope in one of four basic ways:

Language-defined: All scopes are, by default, given the names this and arguments.

Formal parameters: Functions can have named formal parameters, which are scoped to the body of that function.

  • Function declarations: These are of the form function foo() {}.
  • Variable declarations: These take the form var foo;.
  • Function declarations and variable declarations are always moved (“hoisted”) invisibly to the top of their containing scope by the JavaScript interpreter.
  • Function parameters and language-defined names are, obviously, already there. This means that code like this:

Ex:

function foo() {
        bar();
        var x = 1;
    }

is actually interpreted like this:

function foo() {
    var x;
    bar();
    x = 1;
}

It turns out that it doesn’t matter whether the line that contains the declaration would ever be executed. The following two functions are equivalent:

function foo() {
    if (false) {
        var x = 1;
    }
    return;
    var y = 1;
}

function foo() {
    var x, y;
    if (false) {
        x = 1;
    }
    return;
    y = 1;
}

Notice that the assignment portion of the declarations were not hoisted. Only the name is hoisted. This is not the case with function declarations, where the entire function body will be hoisted as well. But remember that there are two normal ways to declare functions. Consider the following JavaScript:

function test() {
    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
    function bar() { // function declaration, given the name 'bar'
        alert("this will run!");
    }
}
test();

In this case, only the function declaration has its body hoisted to the top. The name ‘foo’ is hoisted, but the body is left behind, to be assigned during execution.

That covers the basics of hoisting. The complete 100% credit of this answer goes to ben cherry. I didnt want to post this link in my answer because the links might break and I found this completely informative and a must read for any javascript developer.

Community
  • 1
  • 1
Rajshekar Reddy
  • 18,647
  • 3
  • 40
  • 59