Programming language variables: scope and extent
Join the DZone community and get the full member experience.
Join For FreeThis post examines two aspects of how variables work in programming languages: The scope and the extent of variables. Example source code is given in JavaScript, but should simple enough to be universally understandable.
Static versus dynamic
Before we can start our examination, we need to establish how the workings of variables can be observed. The following are two ways of looking at program code.-
Statically, one examines the program as it exists in source
code, without running it. Given the following code, we can make the
static assertion that function g is nested inside function f.
function f() { function g() { } }
The adjective lexical is used synonymously with static, because both pertain to the lexicon (the words, the source) of the program. -
Dynamically, one examines what happens while executing the program (“at runtime”). Given the following code, f and g have a dynamic relationship, because f invokes g.
function g() { } function f() { g(); }
Variable bindings and their scope and extent
A statementvar x = ...;establishes a binding for the variable x that maps the variable name to a value. In the following, the terms “variable” and “binding” are often used interchangeably. This is less precise, but makes explanations shorter. Bindings are characterized by:
- Scope (a spatial property): Where can the binding be accessed?
- Extent (a temporal property): How long does the binding exist?
Scope: The direct scope of a variable is the syntactic construct “in which” a binding has been created. What constructs form scopes depends on the programming language: any kind of block in most languages, only functions in JavaScript. One has two options when it comes to determining where else a variable is accessible, in addition to the direct scope:
-
Static (lexical) scoping: A variable is accessible from the
direct scope and all scopes lexically nested in it. In the following
example, x is visible in its direct scope, function f, but also in the nested scope, function g.
function f() { var x; function g(y) { return x + y; } }
-
Dynamic scoping: In dynamic scoping, a variable declared in a
function is accessible in all invoked functions. The example below
illustrates dynamic scoping. If f is invoked, which in turn invokes g, then myvar at (*) refers to the binding in f.
function g() { var x = myvar + 1; // (*) } function f() { var myvar = 123; g(); }
- Scopes can be nested. The direct scope can be seen as nesting all other scopes where a variable is accessible. In that case, the nested scopes are called inner scopes, the direct scope is called outer scope. In static scoping, nesting happens by nesting syntactic constructs. In dynamic scoping, nesting happens by recursively invoking functions.
-
Variables can be shadowed. If an inner scope declares a variable
with the same name as a variable in an outer scope, the outer variable
is shadowed by the inner one: it cannot be accessed in the inner
scope (or scopes nested in it), because mentions of it refer to the
inner binding. In the following example, the declaration in g shadows the declaration in f. Thus, x has the value "bar" in g and in h.
function f() { var x = "foo"; function g() { var x = "bar"; function h() { } } }
- Dynamic extent: A variable exists starting with its declaration until its existence is explicitly ended. This usually means that the direct scope of the declaration has finished computing. To handle nested scopes, dynamic extent is often managed via a stack of bindings. Dynamic extent is common in simpler programming languages. JavaScript, however, can do more. Read on.
-
Indefinite extent: In JavaScript, the extent of a variable
starts with its declaration. Its binding exists as long as there is code
that refers to it. Thus, if a function leaves its static scope, it
keeps variables in outer scopes alive if it refers to them. In the
following example, function g keeps x alive, because it leaves its static scope (the function f). Later, once there are no more references to g (such as myfunc), both g and the binding for x can be garbage-collected.
function f(x) { function g() { return x; } return g; } var myfunc = f("hello"); console.log(myfunc()); // output: hello
Related reading
- Scope and Extent (chapter 3 in “Common Lisp the Language” by Guy Steele). This chapter is the source of part of this post.
From http://www.2ality.com/2011/05/programming-language-variables-scope.html
Opinions expressed by DZone contributors are their own.
Trending
-
Redefining DevOps: The Transformative Power of Containerization
-
Building and Deploying Microservices With Spring Boot and Docker
-
Chaining API Requests With API Gateway
-
What Is React? A Complete Guide
Comments