DEV Community

Cover image for Scopes in Javascript
Eckehard
Eckehard

Posted on

Scopes in Javascript

Scopes are a core concept of any programming language: They control the visibility and acessibility of code elements. Seemingly the concepts of Javascript and other languages like C++ are similar, but they are not. So it´s worth to have a deeper look at Javacript scoping.

First of all: Why are scopes so important?

  • Scopes prevent you from accidentially using or changing values outside the scope.
  • Using libraries or larger codebases will let you run into naming conflicts. Scopes help to avoid this.
  • If there is a need to exchange data between different parts of your code, a fine grained scope control helps you to better organize this exchange.

Before we dig into the details, we should have a look, how scopes are organized in other languages like C++.

Scopes in C++

From a birds view, scopes in C++ look similar to scopes in JS: Global variables are visible in all parts of the code, while variables defined in a block or function are local, they are only visible inside the block.

But there are some important differences:

  • In C++, variable definition is done independent of code execution, so the order of execution does not matter for the scope
  • Variables are usually defined outside a block, so the definition is valide everywhere in the block. In JS, block scope just defines, where the scope ends, but not where it starts.
  • Units in C++ are split in two different parts: A header file, where the "interface" is defined, and a code file, that contains all the code and the local definitions. The header may be imported multiple times in other units to make the "external" definitions available to other units. But the code is just compiled once in a project. ES6-modules try to mimic this behavoir, but in fact, it is not the same.

Temporal scoping in Javascript

Javascript is an interpreted language, which makes a fundamental difference: Time of execution matters! In a compiled language, all code is visited before execution. Scope definition and code execution are two separate steps. So, it does not matter, where a code is located in the source or when it is executed. In Javascript, code execution and scope definition occure in the same process. Code is executed step by step and this can affect the scope. A variable definition has no effect unless it is not executed. I call this "temporal scoping" and this can have strange effects. Let´s see an example:

    function f(){
      console.log(a)
    }
    let a="Hello World"
    f() // -> output: "Hello World"
Enter fullscreen mode Exit fullscreen mode

This is valid, although f() is defined before "a". At the time of execution of f(), "a" is already defined.

    let a="Hello World"
    f() // -> output: "Hello World"
    function f(){
      console.log(a)
    }
Enter fullscreen mode Exit fullscreen mode

This is also valid, but for another reason: Javascript uses "Hoisting", so function names are evaluated before a code block is executed. Here we can access f() before it is defined.

    f() // -> Reference Error: Cannot access 'a' before initialization
    let a="Hello World"
    function f(){
      console.log(a)
    }
Enter fullscreen mode Exit fullscreen mode

Here, we can acces f() through hoisting, but f() cannot acces "a", although a was defined before f(). Temporal scoping sometimes causes unexpected effects and can make code hard to understand.

Code execution in C++ and HTML

Beside the fact, that Javascript is not compiled, there are some fundamental differences in the way, the code is executed. Again, we start with C++.

In C++, only one function is executed after a program is launched: main(). The program ends, when main() is left, so programms usually implement some kind of event loop to keep the program running and doing some useful things, until the user decides to finish.

In Javascript, this is different, at least if the code is executed in a browser (And this is still the most common platform to run Javascript). Javascript runs as part of an HTML-file, and Script code can be embedded in different positions in an HTML-file

  • inside the <head> element in a <script> tag
  • As an external javscript file
  • As an external module file, which is similar to a script file, but follows different rules
  • inside the <body> element at different positions mixed with HTML elements.

A typical beginners question is: Does the position matter? Sure, it does!

The order of execution

HTML-pages are evaluated by the browser strictly from top to bottom, all elements are evaluated just as they appear in the file. At the end of the file, the evaluation stops.

We often read, that Javascript is evaluated after HTML is rendered, but this is not true: Execution and rendering just appear in the order they are put in the HTML file. See the following example:

<body>
  <h1 id="headline1">Headline one</h1>
  <script>
    document.body.appendChild(document.createTextNode("this is dynamic code"))
    console.log(headline1.innerHTML)
    console.log(headline2.innerHTML)
  </script>
  <h1 id="headline2">Headline two</h1>
</body>
Enter fullscreen mode Exit fullscreen mode

There are two headlines created in HTML with id´s "headline1" and "headline2". Between, there is a script, that creates a dynamic element. This is the result:

Headline one

this is dynamic code

Headline two

This proves, the script is executed between the two headlines.
And this is the console output:

Headline one
index.html:
Uncaught ReferenceError ReferenceError: headline2 is not defined
    at <anonymous> 
Enter fullscreen mode Exit fullscreen mode

The script tries to print the content of both headlines, but at the time of execution headline2 does not yet exists. So, the script is clearly executed, before headline2 is rendered.

Order of execution - Effects on scope

Code execution in HTML always starts with an empty global scope. While the code is executed, the context slowly fills with definitions. But there is only one single context for all parts of the code.
Let show this in an example:

Source Code

Variable "a" is defined in the head-element, but passes through the code execution until the end of the HTML-file. Alle parts of the code share the same global context as if they where one. Even variable "b" is defined in the external script but is also applied to the global context.

ES6-modules: What is the difference?

ES6-modules have been introduced to get better code encapsulation. So, they have a local scope and do only export, what is explicit marked as "export". But see, what happens with the scope, if you call the module:

Source Code2

(You will need a live-server to run this code, as modules do not executed from a file). As nothing is exported, the life of "b" ends with the scope of "schript.js". But "a" is still valid in the scope of script.js. So, the global context is always imported into the module. I marked the console outputs in the order they appear. This is the console output:

log 1:1
log 3:2
log 4:3
log 2 in schript:4
log 5 in schript2:5
Enter fullscreen mode Exit fullscreen mode

ES6 modules are executed out of order, so they run after the standard script code has finished. The important thing: **the variable "a" is still accessible and will also be updated, but in a strange order.

Lessons learned

Scopes in Javascript depend much on the order of execution. ES6-modules can access everything in the parent context and may also change values there, but the time they are executed is hard to predict. So, encapsulation is far from perfect.

And what is the difference to C++

Units in C++ always start with a clean context, they have not access to the global context of the main program. Everything needs to be imported explicitely into the module to use it. In Javascript, you always need to care, what is defined globally. It does not help to call ES6-modules at the start of a file, as they are executed delayed.

The best advise could be, to leave the global context always empty. But there are some definitions, like the "globalThis" or "window", that are there before anything is rendered. Even though you can, you should not change this variables from within a module to avoid trouble.

Top comments (2)

Collapse
 
corners2wall profile image
Corners 2 Wall

The student is a zoologist. He only knows about fleas. On the exam
question about dogs. Sudent begins:

  • Dogs are furry mammals. Fleas in wool... more about fleas... Teacher:
  • Okay, young man, tell us about cats Student:
  • Cats are furry mammals. Fleas in wool... more about fleas... Teacher:
  • Let's talk about fish Student:
  • Fish are not mammals. Not covered with fur. covered with scales, but if if they were covered with wool, then fleas would be found in it ....
Collapse
 
artydev profile image
artydev • Edited

Thanks Eckehard