3

can anybody explain me why I'm getting different output?

code 1:

var a = 1;

function b() {
  a = 10;
  console.log(a); //output 10
}

b();
console.log(a); //output 10

code 2:

var a = 1;

function b() {
  a = 10;
  console.log(a); //output 10
  function a() {}
}

b();
console.log(a); //output 1

Why I'm getting different output for "a" variable after calling function "b"? Needed some clear explanation what's really happening here?

void
  • 36,090
  • 8
  • 62
  • 107
Nag
  • 806
  • 1
  • 13
  • 28
  • 1
    I’m not sure what you’re not understanding here. You already know about hoisting, even function hoisting. – Sebastian Simon Feb 28 '18 at 06:16
  • my first doubt ! does the function "a" gets executed here? I'm not even calling the function. How it affects the output after calling the function b? – Nag Feb 28 '18 at 06:50
  • @Nag You will execute `a` when you `a()`. – Learn on hard way Feb 28 '18 at 07:14
  • exactly! that's my point! then in both cases, the answer should be 10, because function a is not called! it is good as not present at all, right? Please make me understand, this might be simple. – Nag Feb 28 '18 at 07:26
  • @Nag Looking at the first example, can you tell me what `a = 10;` does? – Learn on hard way Feb 28 '18 at 07:30
  • @Nag Why would calling the function affect variable assignment? The function does nothing. It _is present_, however. It is declared, defined, and in scope; it’s accessible up until it’s redefined at `a = 10;`. Yes, it is not called, but this has nothing to do with hoisting. – Sebastian Simon Feb 28 '18 at 07:33
  • @Nag I am not trying to be rude, just establishing how much do you know so I can explain to you. Hoisting is built on few blocks, and if you don't know them, then you will never understand the hoisting. – Learn on hard way Feb 28 '18 at 07:44
  • @Xufox, what I'm understanding is function "a" trying to re-declare the var a inside the function b. That is why var a value inside the function b is 10 and outside is 1. when i don;t use the function with same name i.e a, the output remains 10 ! https://jsbin.com/rabovemaka/1/edit?html,js,console,output – Nag Feb 28 '18 at 08:01
  • @Learnonhardway, check this https://jsbin.com/himazaxohe/1/edit?html,js,console,output and this https://jsbin.com/rabovemaka/edit?html,js,console,output – Nag Feb 28 '18 at 08:04
  • @Nag There’s nothing trying to re-declare anything. There’s only a single declaration in the function `b`: that of the function `a`. – Sebastian Simon Feb 28 '18 at 08:04
  • @Nag I have checked it. What about it? You still have not answered me a question btw. – Learn on hard way Feb 28 '18 at 08:08
  • @Xufox, my question is simple, remove the function "a" inside function "b", the output is same! but inserting it inside the function "b" is creating the local scope for var "a". why is that? – Nag Feb 28 '18 at 08:11
  • @Nag Because the declaration of `a` is hoisted. Simple as that. Can we assume that you have read other questions like [Surprised that global variable has undefined value in JavaScript](https://stackoverflow.com/q/9085839/4642212) or [JavaScript hoisting function vs function variable](https://stackoverflow.com/q/35253376/4642212)? – Sebastian Simon Feb 28 '18 at 08:19

2 Answers2

5

I would say case 1 is pretty self-explanatory, as the value of a is overwritten by a=10.

In case 2, because of function a() {} and because of hoisting the variable declarations and function definitions are moved up to the closest lexical scope which is function b() for function a() so a is scoped to function b() and changing its value will not affect the value of global a and instead the function a() is overwritten.

Because of which the console.log(a) present outside function b() is logging 1 as global value is not changed.

void
  • 36,090
  • 8
  • 62
  • 107
  • alright, i got it that function a() { } is moved up.something like this. var a = 1; function b() { function a() {} a = 10; console.log(a); //output 10 } b(); console.log(a); //output 1 Now why insertion of function name with same var name "a" is affecting the output after calling function b? if i remove the function "a" output will be 10 as expected ! what that empty function a doing here? Does it get executed? i don' think so – Nag Feb 28 '18 at 06:44
  • 1
    @Nag I believe that by that comment you wrote, you are fundamentally wrong and you have misunderstood how hoisting works. Code does not move up or down, JS just have priority when declaring stuff. Functions first, then the variables. – Learn on hard way Feb 28 '18 at 06:57
  • @void Please avoid using the word `moving` when explaining hoisting. Nothing gets moved in the `creation` phase. Also, `a` function does not get invoked at all. _edit reason: wrong @user_ – Learn on hard way Feb 28 '18 at 07:11
  • @Learnonhardway, please it will be really great if you explain me bit by bit of what is going in my code? also what exactly is hoisting? I'm stuck in this concept. – Nag Feb 28 '18 at 07:13
  • @Learnonhardway, check this https://jsbin.com/rabovemaka/edit?html,js,console,output – Nag Feb 28 '18 at 07:15
  • @Learnonhardway this is what MDN says.. `Conceptually, for example, a strict definition of hoisting suggests that variable and function declarations are physically moved to the top of your code, but this is not in fact what happens. Instead, the variable and function declarations are put into memory during the compile phase, but stays exactly where you typed it in your coding.` So using the term moving is not that a big issue. Read more about it here https://developer.mozilla.org/en-US/docs/Glossary/Hoisting – void Feb 28 '18 at 07:16
  • @void Sure. But **that** you should state in the first paragraph of your answer. `Moving` is a human term and kinda opened for self-interpretation. When OP asked you in comments if `a` is moved up (quite literally), you said yes. – Learn on hard way Feb 28 '18 at 07:19
  • @Learnonhardway, here function a is hoisted, means it is available at the starting of the function b, so the output! but thy this is changing the value of global var a? is should've been 10 in both the cases? – Nag Feb 28 '18 at 07:19
  • @Nag No, because in your _second_ snippet, you’re dealing with a _different_ `a` inside `b`: the one scoped to `function b`. In your _first_ snippet, you’re only declaring one `a` — in the outer scope — so you’re redefining the _same_ `a`. – Sebastian Simon Feb 28 '18 at 08:34
  • @Nag Check my new response. Might help – Learn on hard way Mar 02 '18 at 09:00
  • @Learnonhardway, thanks for that, i'm going through it and trying to understand, before that, i came across this https://stackoverflow.com/questions/40675821/javascript-variable-name-same-with-function-name my question is, does the javascript compiler creates new variable same as that of function name in that scope at the time hoisting? if this is right, it clears all my doubts ! – Nag Mar 04 '18 at 17:27
  • @Nag The correct term is _it assigns identifier_ to that function, meaning that that identifier knows where that function resides in memory. To make things easier, you can always define function via `var` declaration and completely forget about function declarations at all... something like [this.](https://jsfiddle.net/9693xqcn/) – Learn on hard way Mar 04 '18 at 17:58
  • @Nag `var identifier` to clarify what is meant by it. – Learn on hard way Mar 04 '18 at 18:26
0

I believe that our OP has tasted the trade-offs of Internet learning. Accessibility to pieces of information, but no guide to it. Unfortunately, we don't know how much you know and we assume lots of things, and those are that you know few things that are a prior knowledge that ultimately leads to an understanding of what dreaded hoisting is. This is typical how magnets work, and by that, I will provide some keywords that should serve as guidance to your understanding of JavaScript.

Creation Stage + Activation aka Code execution Stage = Execution context

Ok but what are each of those?

Scope Chain + Creating arguments, functions, variables + value of 'this' keyword = Creation Stage and

Assigning values and references to functions, then execute the code = Activation aka Code execution Stage

  • Creation stage

    • Set Scope chain
    • Create object that will contain variables
      • Creating arguments object
      • Search function declarations and for each of them, create property with that name on the object above, and if function name exists then ref pointer value will be overwritten
      • Search variable declarations and for each of them, create property with that name in the object above, also, set its value to undefined. If the name already exists, do nothing.
    • value of this keyword is resolved.
  • Execution stage aka running the code stage

    • Doing functions exe () and assigning values to this = value is assigned.

code 1 Start executing line by line following the algorithm above.

Stage 1: Creation stage

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: `undefined`
  },
  this: //not important for this example
}

And the creation stage is done. We went line by line with the defined instructions above. Now we are in the execution stage where assigning = and executing () happends. Once more we start from the line 1

Stage 2: Execution stage

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 1 // because we did the `=` on line 1
  },
  this: //not important for this example
}

and then on line 8, we found the function execution which means that create new execution context on top of global execution context, and that means we follow the algorithm above once more for that function.

Stage 1: Creation stage

bExeContext = {
  // This is the scope object, and in this object now is placed the global exe context we have worked on before
  scope: {globalExeContext}
  objForVariables = {
    //the is arguments object for this one but its empty, because we have no arguments for this function
    args:{},
    // function declarations are none here
    // variable declarations are none here
  },
  this: //not important for this example
}

Now, we have entered the execution stage where we do () and =. On line 1 it is told that a = 10 which means we need to find a to assign the value to it. But we don't have a in bExeContext object? What now? Now we go into scope and try finding it in there. Sure enough, there is a in our global space and now we assigning 10 to it. We have overwritten it. It will never be the same, and i will bring back the globalExeContext to show you that.

Stage 2: Execution stage

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 10 // look what you have done
  },
  this: //not important for this example
}

now that this is resolved, we go back executing the next line in our b function and that is console.log(a);. We need to resolve what a is and going all again, searching in b gives us nothing, but we have it in globalExeStack. You remember, it is 10. So we log 10. We have reached the end of the function and it is popped off the stack. It is no more.

Now we resume executing the last line of our global code: console.log(a); and easy enough, there is a in globalExeStack. It is 10.

code 2 Stage 1: Creation stage.

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: `undefined`
  },
  this: //not important for this example
}

Stage 2: Execution stage = and () Line one is the same

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 1
  },
  this: //not important for this example
}

and line 8 tells to execute b function.

Stage 1: Creation stage

bExeContext = {
  scope: {globalExeContext}
  objForVariables = {
    //the is arguments object for this one but its empty, because we have no arguments for this function
    args:{},
    // function declarations
    a: `points to function`
    // variable declarations are none here
  },
  this: //not important for this example
}

Stage 2: Execution stage

line 1 tells to find a and assign 10 to it. We are lucky this time because a can be found in our bExeContext.

bExeContext = {
  scope: {globalExeContext}
  objForVariables = {
    //the is arguments object for this one but its empty, because we have no arguments for this function
    args:{},
    // function declarations
    a: 10 // no longer points to `points to function`
    // variable declarations are none here
  },
  this: //not important for this example
}

and that means that we did not have to go to global space to find a. Line 2 of executing b function tells console.log(a);. We need to resolve what a is and sure enough

bExeContext = {
  // blablbalblablbalblablblal     
    // blablbalblablbalblablblalb
    a: 10 // no longer points to `points to function`
    // blablbalblablbalblablblalb
  },
  // blablbalblablbalblablblalb
}

we did found it on bExeContext. We log 10. We finished executing b function and we pop it out of execution stack. It is no more.

We resume evaluating global code now. We finished the b(); which was on the 9th line, now we are on line 10. console.log(a); and to resolve a we have to find it first. To refresh how our globalExeContext looks, here is the last picture:

globalExeContext = {
  //no scope
  objForVariables = {
    //no arguments because its no function
    // function declarations
    b: `points to function`,
    // variable declarations
    a: 1
  },
  this: //not important for this example
}

Sure enough, a is 1. We log 1. We are done.

You see, hoisting is achieved as soon as you run JS code. It is the process of laying down the bits of code, sorting it out if you will. More precisely it was 1.arguments 2.function declarations 3.variable declarations. Hoisting happends when your code is just few line or one line, or without any functions at all because as soon you try and play the part of interpreter and laying out the code, you did the hoisting.