Thursday, May 10, 2012

var x = x || "default val” not getting set properly if x is defined above it


HTML:




<script type="text/javascript">
var x = "overriden";
</script>
<script src="myjs.js"></script>



myjs.js:




$(document).ready(function(){
var x = x || "default val";
alert(x); // this alerts "default val" and not "overriden"
});



For some reason, x is ending up as "default val" and not "overriden" , even tho initially I'm setting it to "overriden" before I even include the script reference to myjs.js.



Any idea as to why this is happening? I'm trying to enable the hosting page to set an override for a variable that's used in an included js file, otherwise use the default val.


Source: Tips4all

10 comments:

  1. What you have after variable declaration hoisting is applied:

    var x;
    x = 5;

    $(document).ready(function(){
    var x;
    x = x || "default";
    });


    It looks at the closest x and sees it's value is undefined which is a falsy value, so x gets set to "default".

    You would be fine if they were in the same scope, because the declarations are always hoisted above assignments so:

    var x = 5;

    var x = x || "default";


    Is actually just

    var x;

    x = 5;
    x = x || "default";




    This was suggested which is completely pointless:

    $(document).ready(function(){
    x = x || "default";
    });


    It will throw a ReferenceError if x is not defined.

    So either do the check in the same scope or do something like:

    $(document).ready(function(){
    var x = window.x || "default";
    });


    Invalid property reads don't cause a ReferenceError but just return undefined instead.

    ReplyDelete
  2. The var x in the function passed to $(document).ready() is hiding the global scope x variable.

    If you just use x = x || "default val";, then the global variable is clobbered. However, it's really a bad idea to have global variables at all.

    ReplyDelete
  3. You are using the var keyword in both places. Try:

    x = x || "default val";


    https://developer.mozilla.org/en/JavaScript/Reference/Statements/var

    From the above link:


    The scope of a variable declared with var is the enclosing function
    or, for variables declared outside a function, the global scope (which
    is bound to the global object).

    ReplyDelete
  4. x is not defined in the scope of the document ready function.

    var x is preparing the variable with the value undefined, so it won't get it from the window scope.

    Try var x = x || window.x || "default val"; instead.

    ReplyDelete
  5. Since you have it stated with a var that does not get the globally defined object from the html. Try removing the var and see if you get a different result, or change the variable within the function to a different letter.

    When you use var within a function the scope of that var is only to the function. Since you have defined the x variable twice when you are doing the comparison it is attempting to use the variable from within the function.

    If you plan to keep the var statement you will need to change the variable name or how you are calling the global x to window.x. The other option is just to drop the var from the function.

    ReplyDelete
  6. The x created on this line:

    var x = x || "default val";


    ...is local to the function it is defined in (i.e. it shadows the global x) .

    Removing the preceding var will cause both x names to reference the same global variable, but this is generally a bad idea. From the previously-linked Wikipedia page:


    This can lead to confusion, as it may be unclear which variable
    subsequent uses of the shadowed variable name refer to.

    ReplyDelete
  7. Because you are again declaring a new var x inside document.ready() which is another scope than global. Try this

    $(document).ready(function(){
    x = x || "default val";
    alert(x); // this will now alert "overriden"
    });

    ReplyDelete
  8. You are declaring var x in your function which creates local storage for x and hides previous values.

    Call it var y, or do as karim79 suggested.

    ReplyDelete
  9. Your var x in the second one is local to that function, and isn't the same as window.x (which is what you've defined in the former block).

    ReplyDelete
  10. the keyword var means that you've just created a local variable. So basically what is happening is that:


    You declare (in your first block ) x as "overridden"


    Memory:

    x -> "overriden"



    You set the callback to document.ready


    Memory:

    x -> "overriden"


    (after the document is loaded) 3. you run your anonymous function, initialising x with var

    Memory:

    x->"overriden"
    [IN SCOPE]
    anonymous_function.x -> "default_value"
    [/SCOPE]




    "BUT WAIT", you might say, "the code I wrote is var x= x || "default var" so what should happen is local_x=global_x || "default value ,right?

    Unfourtunately Javascript doesn't work like that. In javascript, we have function scope, instead of the more widely used block scope. So when you write:

    f=function(){
    var x=y || "default value";
    var y=3;
    alert(x);
    }


    what'll be interpreted in javascript is :

    f=function(){
    var x;
    var y;
    x=y || "default value";
    y=3;
    alert(x);
    }


    so running y=10; f(); will give you "default value" on the prompt.



    So taking off your var will work to fix your problem here, but just remember in general that all your variable declarations are done "at the beginning" of a function, so scope is immediately changed when entering a function.

    ReplyDelete