1

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'.

sbr
  • 4,735
  • 5
  • 43
  • 49
  • 2
    JavaScript doesn't have block scope. Your `var aptNum` in the `with` is a variable local to the enclosing function. The properties of the object provided to `with` becomes accessible as variables within the block, which is why you get `2`. You can't add new properties to the object using `var`, though you can update existing properties without referencing the object directly. –  May 15 '13 at 23:45
  • @squint thanks. this seems to be somehow creating a block scope with 'with' object properties populated in the local scope. and then scope goes away after the block. but since there is no block scope in JS, this is a strange behavior – sbr May 15 '13 at 23:49
  • Yes, it's behavior is strange and confusing, which is why people recommend to avoid it. It can be useful though. It is a scope of sorts, but it's not a typical variable scope. The `var` isn't constrained to the block. –  May 15 '13 at 23:51

5 Answers5

2

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)
    }
}
bfavaretto
  • 71,580
  • 16
  • 111
  • 150
  • so, basically, for aptNum inside the with block, it updates the address.aptNum but for x it updates the local 'x'. if we check the values outside the block for x and aptNum, this proves it. http://jsfiddle.net/ftK2Z/4/. Thanks. this makes sense. – sbr May 16 '13 at 00:57
  • Exactly. It also proves how with statements can be confusing. – bfavaretto May 16 '13 at 02:21
1

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/

Rick Viscomi
  • 8,180
  • 4
  • 35
  • 50
  • I am not concerned with how i can have x equal to x. Actually , i am trying to demonstrate the scope resolution peculiarity here. The question is why the difference in resolution for x vs aptNum – sbr May 15 '13 at 23:34
  • That's been explained already by showing the misuse of `var` keyword on `x`. – Rick Viscomi May 15 '13 at 23:35
1

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

Dave
  • 95
  • 5
0

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.

Yaw Boakye
  • 10,352
  • 1
  • 17
  • 25
0

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.

Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • I see your point. But the thing that makes it interesting is: aptNum is not output as 'undefined'. It is resolving the value from the address.aptNum which is '2' – sbr May 15 '13 at 23:36