Question

There seems to be a strange behavior in Javascript with hoisting and scoping , when using 'with' keyword.

I understand the usage of 'with' is considered taboo, but came across this in one of the frameworks and had to deal with it. But nonetheless,

Could anyone explain why if we have a 'with' block , the variables that are declared but undefined are resolved using the with object but this is not the case with the closure variables.

To demo, see the output of the following code: http://jsfiddle.net/ftK2Z/1/

var x = 90;

function test1(){
    var address = {
         street: 'Haight',
         aptNum: 2
     };


    with (address){
      alert(aptNum + ":" +  x); // this  outputs 2 : undefined. 
      var aptNum = 100,
      x = 10 ;
    }
}

Checked with google chrome.

Note: that I understand JS hoisting and see why having var statements after the alert is an issue but the main thing I am trying to figure out it is , during scope resolution at the alert statement, both 'aptNum' and 'x' should be undefined in local scope due to hoisting, so they should be taken as 'declared but not defined'.

But still, aptNum is output as '2'.

Was it helpful?

Solution

Inside the with block, the variable aptNum is shadowed by address.aptNum, and x refers to the local variable (never address.x, as there isn't such a property).

Considering that, and hoisting, your code is equivalent to:

var x = 90;
function test1(){

    var aptNum; // hoisted
    var x;      // hoisted

    var address = {
        street: 'Haight',
        aptNum: 2
    };

    with (address){

        // any reference to aptNum or street inside the with block
        // is actually referencing address.aptNum and address.street

        alert(aptNum + ":" +  x); // this  outputs 2 : undefined. 
                                  // as expected, because there is no address.x
                                  // and the local x is undefined at this point

        aptNum = 100; // this assigns to address.aptNum
                      // the variable aptNum is shadowed by address.aptNum

        x = 10; // this assigns to the local x inside the function 
                // (again because there is no address.x)
    }
}

OTHER TIPS

From fiddle:

with(address){
    $('#output').text('aptNum : ' + aptNum + ",   x : " + x ); 
    var aptNum = 100,
        x = 10 ;
}

The variable x is being redeclared in the body of the with statement (see preceding var keyword). Variable declarations are hoisted, so x is being defined temporarily to undefined, then finally redefined to 10 after the value has already been logged.

In this updated fiddle, I changed the variable declaration statement to a simple assignment statement, which produces the expected behavior of logging the value of x to be 90. See http://jsfiddle.net/ftK2Z/3/

Using with is not recommended, and is forbidden in ECMAScript 5 strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable.

It does not like having a global variable. I think its due to the fact x is outside the scope of the address object. You can pass the parameters into the function and it works.

var x = 90;
test(x);

Example: jsFiddle

More info on with JavaScript with

I guess you called x (which WILL be expected as a property of address) before even declaring x. x in the with scope doesn't refer to the x declared outside the function. The engine is trying to read the value of address.x which doesn't exist, of course. That's the reason. Concerning with, since aptNum is 're'-declared as a variable in that scope, it DOESN'T refer to address's aptNum.

The code

alert(aptNum + ":" +  x);
var aptNum = 100,
      x = 10 ;

is var-ing aptNum and x within the with. var is hoisted so it can also be considered as

var aptNum, x;
alert(aptNum + ":" +  x);
aptNum = 100, x = 10;

It is now easy to see why they are undefined. You probably didn't want to use the var here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top