Topera
Topera

Reputation: 12389

why ajax callback is setting undefined in my local variable?

I found a bug in my site, because of this strange behavior. The second function alerts "undefined" instead of "one".

function test() {
    var myParam = "one"
    if (false) {
        var myParam = "two";
    }
    alert(myParam);
}
test(); // alert "one"


function testAjax() {
    var myParam = "one";
    $.ajax({
        url:    "http://url-to-get-error",
        error:  function(){
            if (false) {
                var myParam = "two";
            }
            alert(myParam);
        }
    });
}
testAjax(); // alert "undefined"

And if I comment block bellow, alerts "one" correctly. Someone knows why?

if (false) {
var myParam = "two";
}

See in http://jsfiddle.net/EaW8D/2/.


UPDATE

Guys, I know how to to solve the problem, thanks a lot!

But the question is WHY js is setting undefined in a block that NEVER run (if (false)).

Upvotes: 1

Views: 426

Answers (4)

apsillers
apsillers

Reputation: 115950

All variable declarations of the form var myParam are hoisted to the top of the function scope. Even if they are in inaccessible blocks or at the end of the function, a function with a var declaration in it anywhere will treat all instance of that variable name as local.

Consider this handler:

    error:  function(){
        alert(myParam);
        var myParam;
    }

It will also cause a undefined alert, because the var myParam at the bottom means, "Treat all instance of myParam that come both before and after this declaration as local variables in this function." The same hoisting that applies in this case applies in your inaccessible-logic-block case.

Upvotes: 2

Felix Kling
Felix Kling

Reputation: 816442

Someone knows why?

You are re-declaring myParam inside the error handler, shadowing the variable with the same name in the outer scope. Variable declarations are "hoisted" to the top of the function, so your code is effectively the same as:

error:  function(){
    var myParam;  // <- declaration
    if (false) {
        myParam = "two"; // <-  initialization
    }
    alert(myParam);
}

and since the if block is never executed, myParam will be undefined (the default value for uninitialized variables).

Here is a simple example to proof this behaviour. If the variable was not hoisted, foo would become a global variable (assuming non-strict mode) and a property of window, but it does not:

(function() {
    if(false) {
        var foo = 'bar';
    }
    foo = 'baz';
    console.log(window.foo); // yields `undefined`
}());

To solve this problem, simply remove var, so that myParam refers to the variable declared in the outer scope, testAjax:

error:  function(){
    if (false) {
        myParam = "two";
    }
    alert(myParam);
}

Upvotes: 5

Gabe
Gabe

Reputation: 50493

Because you're creating myParam as a new var again

function testAjax() {
    var myParam = "one";
    $.ajax({
        url:    "http://url-to-get-error",
        error:  function(){
            if (false) {
                myParam = "two";
            }
            alert(myParam);
        }
    }); 
}
testAjax(); // alert "undefined"​

http://jsfiddle.net/mKdhx/

Upvotes: 2

Jerzy Zawadzki
Jerzy Zawadzki

Reputation: 1985

in second situation your myParam=one and myParam=two are in different blocks - and moreover callback of ajax error is not "under" testAjax in terms of variable visibility - it's asynchroneus. You would need myParam in global scope

Upvotes: -1

Related Questions