0

I run into a bit of confusion regarding scoping with respect to where a callback is defined.

function test(){

    var b = 3
    var fun = function(){

        var printingFunction = function(){
            console.log(b)
        }
        printingFunction()
    }

    return fun
}

test()() //prints 3, as expected because of closures

However, the following doesnt work

function test(){

    var b = 3
    var fun = function(cb){
        cb()
    }

    return fun
}

test()(function(){console.log(b)}) //b is not defined

I would expect that since the function is passed as an argument and has not been defined before, its definition takes place inside 'fun' and therefore it would have access to b. Instead, it looks a lot like the function is first defined in the scope where its passed and THEN passed as an argument. Any ideas/pointers?


EDIT: Some extra pointers.

someFunction("a")

We couldn't possibly claim that "a" is a definition. What happens here implicitly is that "a" is assigned to a variable named by the argument name so var argumentNameInDefintion = "a". This happens in the body of someFunction.

Similarly we cant claim {} is a definition in : someFunction({}). So why would:

someFunction(function(){})

decide that function(){} is a definition is beyond me. Had it been

var a = function(){}
someFunction(a)

everything would make perfect sense. Maybe its just how the language works.

Return-1
  • 2,329
  • 3
  • 21
  • 56
  • Possible duplicate of [*How do JavaScript closures work?*](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – T.J. Crowder Mar 05 '18 at 08:40
  • Not really. I understand closures, indeed the very first example is me displaying that. This is a question about where the function is defined when passed as an argument directly. – Return-1 Mar 05 '18 at 08:41
  • 1
    All due respect, if you really understood closures, you'd know why the second example doesn't work. – T.J. Crowder Mar 05 '18 at 08:43
  • 1
    Actually the scope of the callback function is where it is defined, no matter through what calls it went through, it can only access things from it's scope (other than arguments passed in) – Icepickle Mar 05 '18 at 08:43
  • @T.J.Crowder how does this anything have to do with closures... closures is a mechanism that operates based on where the function is defined.. I dont even know whether the function is defined in the global or 'fun' scope. Been trying to get an a answer for this for quite sometime but everyone keeps sending me beginner links about how closures work which is really a thing that happens on top of the definition. – Return-1 Mar 05 '18 at 08:50
  • People keep sending you at those things because they contain your answer. Again: Variable resolution depends on where a function is **defined**, not where it's called from. The reason your first example works is that the function is a closure formed by **creating** your function in a context where `b` is in-scope. The reason your second doesn't is that the function *isn't* created in a context where `b` is in-scope. Closures **are** the answer to this question; the sooner you accept that, the sooner your understanding with increase. – T.J. Crowder Mar 05 '18 at 08:58
  • Thank you for your patience. If by "where the function is defined" you mean **where in the source code** then thats the end of that. And this turns out to be the case even though this clearly **contradicts other similar operations**. Again look at `something("a")` for example. Here `"a"` isnt a definition on its own as `{}` isnt a definition on its own. These are just values. so why `something(function(){})` should be different? I guess thats just how the language works. – Return-1 Mar 05 '18 at 09:06
  • actually `"a"` is a definition of a constant value, same thing with a function. So it knows exactly what it knows at time of definition – Icepickle Mar 05 '18 at 09:24
  • 1
    Possible duplicate of [How do JavaScript closures work?](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Icepickle Mar 05 '18 at 09:27
  • if in javascript, "a", {} and function(){} are considered definitions on their own and something("a") implies two definitions, one in the function body and one when we type "a" then everything makes sense. – Return-1 Mar 05 '18 at 09:35
  • 1
    Yep, they are constants, hard coded constants – Icepickle Mar 05 '18 at 09:36

3 Answers3

6

Scoping in JavaScript is lexical. If you look at your examples, you can see that where printingFunction is defined, lexically (e.g., in the source text) b is declared in a containing scope. But in your second example, it isn't. That's why b can't be resolved in your second example but can in your first.

The way it works is that when a function is created, it has a reference to a conceptual object containing the variables and such in the scope in which it's created (which has a fancy name: "Lexical Environment object"); and that object has a reference to the one that contains it. When looking up a variable reference, the JavaScript engine looks at the current lexical environment object and, if it finds the variable, uses it; otherwise, it looks to the previous one in the chain, and so on up to the global one.

More details can be found:

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Yeah but when you pass an argument to a function directly like so fun("a") that value is assigned to a variable which lives in the function's body and doesnt exist elsewhere. So assuming that functions as arguments work in the same way wouldnt be such a stretch i suppose. – Return-1 Mar 05 '18 at 08:43
  • 1
    What matters is where the function is defined, not where you pass it to. If you think about it, having the function's variable resolution depend on where you **called** it would be chaotic at best (and would mean most of the places we use closures wouldn't work). – T.J. Crowder Mar 05 '18 at 08:45
0

The issue stems from me not understanding that function(){} on its own is a definition and so is "a" and {}. Since these are definitions then the definition scope of the function passed is appropriately placed where it is and the world makes sense again.

Return-1
  • 2,329
  • 3
  • 21
  • 56
0

In first case, it is forming a closure and has access to the variable "b" but in second case it does not form a closure at all. If you put a debugger just before the cb() inside the function, you will notice that there is no closure formed and the reason of that being the callback function is suplied to the function as an argument and it becomes local to that function which does not have any knowledge of the variable b.

Think of it as two different functions, one in which it has a local variable "b" and in other no local variable but we are trying to access it which throws the reference error.

Yash Kumar
  • 13
  • 1
  • 7