JavaScript for VBA Developers: Scope


In VBA, the three scopes available for variables are public, module and procedure – usually referred to as Global, Module-level and Local.  Global variables are recognised by every module in the active workbook. Module-level variables are available to all of the procedures in that module, but are not available to procedures in other modules.  A Local variable is recognised only within the procedure in which it is declared

JavaScript has two scopes – Global scope and Function scope.  Global scope operates in a similar fashion to VBA’s Global scope in that such variables can be referenced from anywhere in the code. Function scope, however, has no direct analogy in VBA.  Function scope, while bearing some similarity to VBA’s Local scope, allows variables declared within a function to be available not just within that function but also to functions that are defined within that function, ie. nested/inner functions have access to variables declared in their parent function, grandparent function, great-grandparent function and so on – all the way up this ‘scope chain’ to the Global scope.

The Scope Chain

The scope chain is a list of objects that define the variables that are in scope. When the interpreter starts to resolve a variable, it looks at the first object in the chain.  It it doesn’t find it, it looks at the next object in the chain.  If that doesn’t have it, the search moves on to the next object, as so on until it arrives at the Global object where, if it fails to find the variable, it throws a ReferenceError.

Each function has its own object.  So, a function defined on the global object has two objects in its scope chain – itself and the global object.  A function defined within another function has three or more objects in its scope chain – itself, the objects of its outer function(s) and the global object.  

The following code illustrates the scope chain:

<!doctype html>
<html>
  <head></head>
  <body>
    <p id="p1"></p>
    <p id="p2"></p>
    <p id="p3"></p>
    < script type="text/javascript">
 
      var a = "global scope";
      document.getElementById("p1").innerHTML = a;
 
      function outerFunc() {
        var b = "outer function scope";
        document.getElementById("p2").innerHTML = a+"-"+b;
        function innerFunc() {
          var c = "inner function scope";
          document.getElementById("p3").innerHTML = a+"-"+b+"-"+c;
        }
        innerFunc();
      }
 
      outerFunc();
 
    < /script>
  </body>
</html>
Note: I’ve put leading whitespace in the above ‘script’ tags.  Wordpress doesn’t like its bloggers to run their JavaScript on reader’s web pages so if I’d put </script> it would be removed by WordPress.  Consequently, if you copy and paste the above into a blank text file, don’t forget to remove the whitespace in the ‘script’ tags before you load it into your browser.

A consequence of the scope chain is that, while inner functions have access to variables of their outer functions, the reverse is not true.  To prove this, change the line:

document.getElementById("p2").innerHTML = a+"-"+b;

to:

document.getElementById("p2").innerHTML = a+"-"+b+"-"+c;

and you’ll see things don’t work properly.  If you look at the console output in the dev tool you’ll see an ‘is not defined’ error.

Thinking about scope in this way – ie. it being a series of linked objects that contain variables – helps us to understand another curious feature of the language: Hoisting.

Hoisting

In VBA, the compiler won’t let your code refer to a variable before it has been declared.  JavaScript has no such constraints.  In JavaScript, a variable referenced before it is declared will not give an error.  Another way to think of this, is that the scope chain is determined before the code in that current scope is executed.

Consider the below code:

<!doctype html>
<html>
  <head></head>
  <body>
    <p id="p1"></p>
    <p id="p2"></p>
    < script type="text/javascript">

      document.getElementById("p1").innerHTML = "a = " + a;

      var a = "Hello World!";

      document.getElementById("p2").innerHTML = "a = " + a;

    < /script>
  </body>
</html>

If you run the above you’ll get the results:

a = undefined

a = Hello World!

Notice that you don’t get an error.  When JavaScript enters a new scope it binds the set of identifiers defined by the declarations contained within that scope.  In practice,  this means a new object is created (called an Environment Record) and all of the variables are added (bound) to it.  If a variable exists on the Environment Record but a value has not been assigned to it, JavaScript uses the ‘undefined’ value, in lieu.  It’s important to remember that a variable must be declared at some point in the scope in order to be bound to the Environment Record.  If it’s not declared, it’s not on the Environment Records.  Consequently, referring to such a variable results in a ReferenceError:

<!doctype html>
<html>
  <head></head>
  <body>
    <p id="p1"></p>
    < script type="text/javascript">

      document.getElementById("p1").innerHTML = "a = " + a;

    < /script>
  </body>
</html>

The above code results in a blank page.  If you check the Console in the dev tool (F12 in IE, Ctrl+Shift+I in Chrome) you’ll see a ReferenceError.  It’s also worth remembering that the rules governing variable hoisting operate in the same way for functions:

<!doctype html>
<html>
  <head></head>
  <body>
    <p id="p1"></p>
    <script type="text/javascript">

      myFunc();

      function myFunc() {
        document.getElementById("p1").innerHTML = "Hello World!";
      }

    < /script >
  </body>
</html>

Here the call to myFunc still works as expected even though it’s not defined (declared) until after the call.

While Hoisting is a feature of the language its use is not considered good practice and you should maintain the habit of declaring your variables before they’re referred to by your code.

 

Conclusion

While VBA has 3 scopes, JavaScript has 2: Global and Function.  The Scope Chain allows nested functions to access the variables in its outer functions as well as those in the global scope.  In JavaScript you don’t need to declare a variable before using it, but relying on this is discouraged.

The aim of this blog was to explain to a VBA developer who is unfamiliar with JavaScript, how ‘scope’ operates in the latter compared to the former.  If anything was unclear or you think I’ve missed something, please leave a comment and I’ll see if I can remedy the gap.

Categories:JavaScript, VBA

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: