Weird Variable Scope ?

Hey there, I’ve been learning Javascript for about a week now and I have this thing going:

var input = {
        
        inputNumber: "0",
        bool: true,
        
        setInputNumber: function(id){
            return function(e){
                   alert(inputNumber); //undentified
            }
        }
}

So my question is, why I can’t acess the inputNumber atribute from inside the function i’m returning ? I already read the Variable Scope article here, so I probably missed something. Thanks for the help :slight_smile:

This is one of the confusing parts about the bracket characters - they have dual meanings. They 1) define code blocks (which themselves have different scope behaviors) and 2) are used to in object literals for property definitions.

The big difference being, when used in an object literal, such as input = { ... } there is no scope. All the brackets are doing is marking the start and ending point of where the properties are defined. Ultimately, what your code resolves to is something more like:

var input = new Object();
input.inputNumber = "0";
input.bool = true;
input.setInputNumber = function(id){
    return function(e){
        alert(inputNumber); //undentified
    }
};

In that format, its clear to see there is no inputNumber within the current scope that can be accessed. It’s only accessible directly through the input object because that’s where inputNumber is being defined.

This can get confusing when dealing with functions in object literals like this, because naturally you want to look at the code and consider each {...} block a scope, but they’re not. The object literal syntax is a shorthand for property definitions only and do not create a scope within which those properties can be accessed.

2 Likes

I see ! Thank you ! I learned OOP with Java, so dealing with it in Javascript feels a bit weird haha

You’ll find some similarities here and there, but other things will be completely different. There are some severe limitations with OOP in JS which take a while to get your head around, but once you understand prototypal inheritance, it makes sense. It doesn’t make it better, but at least you grasp what’s going on which helps you make decisions about your code.

If you’re looking for a smoother transition, you might want to consider TypeScript which is a JS superset but handles some things with OOP a little better (or at least calls you out on doing things you shouldn’t) and includes type annotations among other things.

2 Likes

This is a great explanation, sen! I knew that inputNumber would return undefined, but my reasoning for why was flawed. Your snippet helped clarify this! :stuck_out_tongue:

1 Like

What was your original reasoning?

It’s silly. I had assumed it would check in the global scope and just stop. Not sure why I thought that. I’m probably going more insane.

Who would’ve thought that was possible! :run_in_fear:

1 Like

That’s not far off from what’s happening. Since the function is defined directly within the global scope, there is no additional scope except for its own local scope to check for that variable. So the pecking order for scope checking would be: local scope (as always), global scope. And since its in neither, the variable comes back with an undefined value.

Actually, that’s not exactly true. I just realized that the variable is in a function returned by another function, so there is that additional scope in there: local (function(e)), parent function (function(id)), and then global.

But if you want to be technical about it, that scope isn’t checked in the same sense that the global scope is. When the function is created the parent function scopes are checked for variables to bind to the closure. When not found, lookup then resorts to resolving what’s in global, if anything. So really, @kirupa, you’re right. Global is checked and then it stops, because its when the function is created it found that there are no parent scopes to bind a inputNumber from, so the lookup becomes a lookup from global and then that’s it.

1 Like

Thank you @senocular, now scopes and closures became a bit more clear for me. At first I solved it by simply doing something like alert(input.inputNumber), but that’s a litle unintuitive. After your explanation a tried doing:

 input.setInputNumber = function(id){
 var number = this.InputNumber;
    return function(e){
        alert(number);
    }
}

This makes more sense to me, Kirupa had already showed a similiar example when he talked about Objects but the returned Function tricked me. :disappointed_relieved:

1 Like

This is a pretty good way of doing it. This gives you a reliable closure variable, number, which is defined in the parent function of the function returned for better encapsulation. Using input.inputNumber would be less reliable because you’re bound to the variable in the global scope which someone could come along and delete, or rename or something else, causing the reference to break in the function.

var input = {
        
        inputNumber: "0",
        bool: true,
        
        setInputNumber: function(id){
            return function(e){
                   alert(input.inputNumber);
            }
        }
}
var f = input.setInputNumber(5);
f(); // 0
input = null;
f(); // Error

At this point, the only concern with this approach is setInputNumber context - another wonderful issue to deal with in JS :smiley:

1 Like