Some Quick Notes On Variable Scoping

Here's a question I got a while back:

In a VBS or ASP file, the following code doesn't work, which I understand:

Option Explicit
s = "hello"

However, the following code works fine:

Option Explicit
s = "hello"
Dim s

Why can we use a variable before declaring it in VBScript?

A similar situation exists in JScript. This is illegal:


But this is legal:

var s;

What's up with that? Well, let me ask you this -- if you think that looks weird, why do you think this looks normal?

Dim s
s = Foo(123)
Function Foo(x)
  Foo = x + 345
End Function

There the function is being used before it is declared, but that doesn't bug you, right?

Similarly, variables can be used before they are declared. The behaviour is by design. Variable declarations and functions are logically "hoisted" to the top of their scope in both VBScript and JScript.

In JScript, both functions and variables may be redeclared at any time. In VBScript, declaring a variable twice in the same script block is illegal, but redefinition in another block is legal. Procedures may be redeclared at will except if the procedure is in a class, in which case redeclaration is illegal.

OK, that stuff is reasonably common knowledge, but it gets weirder. Did you know that this is legal in VBScript?

s = Foo(123)
If Blah Then
  Function Foo(x)
    Foo = x + 345
  End Function
End If

Not recommended, but legal.  There's a sad story about why that's legal which I might tell at another time.  Suffice to say that it involves ASP pages, a bug, and a rather recalcitrant online news service.  (This behaves as though the function were declared outside the conditional -- you can't do conditional function definition in VBScript, sorry.)

There was a long internal debate over these variable declaration issues back in 1996. There was an even longer debate in the ECMA committee over exactly what the hoisting algorithm should be in ECMAScript. I once knew every such argument about program language syntax in all the scripting languages of men, elves and dwarves -- even now, a score of them come to mind! But it has been eight long years since 1996. I have seen many battles and many fruitless victories. In short, recalling the details to mind is difficult; it's all pretty much a blur now.  I'm going to have to rely on the spec.

In JScript, the hoisting spec is as follows (Section 12.2 of ECMA specification 262, Revision 3)

If the variable statement occurs inside a FunctionDeclaration, the variables are defined with function-local scope in that function […] Otherwise, they are defined with global scope […] Variables are created when the execution scope is entered. A Block does not define a new execution scope. Only Program and FunctionDeclaration produce a new scope. Variables are initialised to undefined when created. A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.

Note that this implies that this silly program

  var v = 123;

is semantically exactly the same as

var v;
v = 123;

Waldemar Horwat's

proposal for ECMAScript 4 further complicates the hoisting algorithm. In E3 there are only global and function scopes, and declarations are hoisted to the top of the “nearest“ scope. In the proposed E4 spect there are global, package, class, function, block, for-statement and catch-clause scopes. In general, declarations are hoisted to the top of their scope, but for backwards compatibility, sometimes variable declarations have to be hoisted to the nearest global, package, class or function scope.

I want to talk some more about the lack of C++-style block scopes in JScript, but that will have to wait for another day.


Comments (9)

  1. grey says:

    vb, javascript, etc. are not lexically scoped.

    at one point in time, i remember the following bombing out on me:

    Recurse(1, 10)

    Function Recurse(current, stop)

    Dim interval

    interval = 1

    If current <= stop Then

    Recurse(current + interval, stop)

    End If

    End Function

    The second time the function was called, it puked that the variable "interval" was already declared.

    This leads to showing that vb is not lexically scoped, and as such, I believe all Dims, etc. are pulled in first during runtime. Hence why you have to do a ReDim at times. It’s a one-time declaration for a variable name for the entire life of the execution.

  2. Eric Lippert says:

    No, VBScript and JScript are lexically scoped. (The "with’ block in JScript leads to some dynamism in scope resolution, but lets ignore that for now.)

    Your program — once I removed the four syntax errors — compiles and runs just fine on my box.

    (For those following along at home: in a "lexically scoped" language, the binding between a variable name and the scope containing the variable can be determined from the compile-time text of the program. In a "dynamically scoped" language, a given name can be bound to a different scope depending on runtime conditions. Lisp is dynamically scoped.)

    >Hence why you have to do a ReDim at times

    No, redim resizes an array. It doesn’t redeclare a variable.

    I think you may be misremembering something, or have misinterpreted an error message somewhere along the way. Can you give me an example of a VBScript program that shows the bad behaviour that you describe?

  3. Ricky Dhatt says:

    Your link to ECMAScript 4 is mangled. Maybe you meant: ?

    Since all I have been doing recently is application scripting with JScript, this is *great* stuff. Keep it up!

  4. Peter Torr says:

    Oh boy, you don’t want to know how many hours we spent talking about hoisting in ECMA meetings.

  5. Bob Riemersma says:

    I sure wish that "hoisting" had not been done in VBScript. Not to be insensitive about it, but I could (almost) hardly care less what was done in JScript. I’ll save time by keeping my rant about "take a stab at what the coder meant" languages and language processors to myself. The semicolon thing has been beat to death.

    My pain comes from all of the VBScript written by others that I’ve had to compile as VB6 over the years. If only I’d taken the time to write something to automate the hoisting. I already had a "grabber/packager" for the embedded literal HTML and the wrapping of "outer scope" code as Sub Page or whatever anyway. So much for thinking (every time) that "this is the last time."

    Oh well, such code invariably needs a lot of hand-tweaking anyway to get rid of bugs, dead code, and other absurdities.

    Still, how the heck did that philosophy ever get incorporated into VBScript?

    BTW: ReDim was always an odd creature. Most bizarre to me was that it could be used in place of Dim, where I think it had a special significance in regard to storage allocation in the QB/PDS/VBDOS context. Firing up VBDOS just now I see it’s there all right. Remember all that DGROUP vs. "far memory" stuff? Thanks for nothing Intel.

    Then there’s the seldom used (today) Erase statement… but I digress.

  6. Eric Lippert says:

    > Still, how the heck did that philosophy ever get incorporated into VBScript?

    The decision was before my time, and somewhat controversial at the time. I have always been opposed to it — however, we’re stuck with it now.

    And yeah, Redim and Erase are both a little weird.

Skip to main content