DEV Community

Cover image for 🚀How JavaScript Works (Part 2)? The lexical scope mechanism of JavaScript
Sam Abaasi
Sam Abaasi

Posted on

🚀How JavaScript Works (Part 2)? The lexical scope mechanism of JavaScript

Understanding lexical scope is essential for mastering JavaScript, as it dictates how variables are accessed and resolved at compile time. This article explores the core principles of lexical scope, delving into the definition, how it shapes variable behavior, and its role in the JavaScript two-pass system. We'll also examine the creation of scopes through functions and blocks and explore key concepts like shadowing, global variables, strict mode, types of errors, undeclared vs. undefined variables, nested scopes, and the role of arguments and parameters in JavaScript functions.

Table of Content

Lexical Scope Definition

Lexical scope consists of two key components: lexical and scope. Lexical refers to the lexing phase of compilation, which we discussed in a previous article. But what exactly is scope?

Scope is the region where JavaScript searches for and accesses variables.

It answers two essential questions:

Key Questions

Lexical scope helps answer two key questions:

  • What are we looking for? Lexical scope is primarily about finding identifiers, such as variables or functions.

  • Where are we looking for them?
    Lexical scope defines the location where identifiers are accessible in your code.

In essence, we're primarily searching for identifiers, such as variables and functions, within various scopes in your code. Variables play two roles:

  • Receiving the assignment of a value Variables in this role act as targets, where values are assigned, like x = 42 or myFunc = function func(arguments) {}.

  • Retrieving a value from the variable Variables in this role serve as sources, where values are retrieved, like console.log(y) or function calls, such as ask().

During the code's execution, JavaScript engine processes scopes, asking two fundamental questions:

  • What is the position of this variable within the scope?
  • To which scope does it belong?

JavaScript's Two-Pass System

Understanding how JavaScript handles lexical scope involves recognizing that JavaScript is a two-pass system:

  1. process the code first: Javascript processing of lexical scopes and putting all of these identifiers, into their correct scope in the parsing stage where it goes through all of our code, produces this abstract syntax tree (AST) and produces a plan. This plan is executable code that is handed off to be executed by the other part of the JavaScript engine (Virtual Machine),

  2. execute it: JavaScript virtual machine, which is embedded inside of the same JavaScript engine. It interprets all that plan and use all that information to execute the code, and one of the things that it interprets is, whenever it enters a scope it creates all the identifiers according to what the plan told it to do and locates memory for them

Organizing Scopes with Functions and Blocks

In JavaScript, lexical scopes are created when the code encounters functions and blocks (curly braces {}).

  • Functions:
    When you declare a function, it creates a new scope. Functions can be nested within other functions, forming a hierarchy of lexical scopes.

  • Blocks:
    Blocks defined by curly braces {} in constructs like if, for, and while statements also introduce new lexical scopes.

How JavaScript Creates Scopes

When JavaScript encounters variable or function declarations, it creates scopes and places those identifiers within the appropriate scope. Consider the following code:

var teacher = 'kyle';

function otherClass() {
    var teacher = "saman";
    console.log("Welcome!")
}

function ask() {
    var question = "why";
    console.log(question)
}
Enter fullscreen mode Exit fullscreen mode

In this code, you can observe how JavaScript creates scopes and assigns identifiers:

  • Variable teacher is assigned in the global scope.
  • Function otherClass creates a new scope, and a local variable teacher is assigned in that scope.
  • The function ask also creates a scope, where the variable question is defined.

In lexically scoped languages like Javascript, all the lexical scopes and identifiers, are determined at compile time not in run time. It is used at run time but determined at compile time, this behaviour allows the engine to be much more effective in optimising because everything is known and it is fixed.

After all scopes are determined during the parsing stage, JavaScript knows the scope of each identifier. This knowledge helps optimize execution since everything is pre-determined at compile time.The decision about scopes are author's time decisions

Shadowing

When you have multiple variables with the same name at different scopes, it is called shadowing. This means the innermost variable with the same name takes precedence when accessing that identifier. For example, if you have a local variable named x inside a function, it shadows an outer variable x.

Global Variables and Strict Mode

JavaScript provides a mechanism for dynamic global variables (auto globals) that are automatically created if you try to assign to a variable that has never been formally declared. This behaviour occurs when the variable is not declared in any accessible scope. However, auto globals are discouraged in favour of explicitly declared variables as they can lead to performance issues.

Strict mode in JavaScript enforces stricter error handling and helps catch potential issues early in development. It's not always enabled by default but is commonly used in modern development tools and transpilers. Inside class and ES6 modules, you don't need to explicitly declare "use strict"; strict mode is assumed.

Types of Errors

JavaScript can throw various types of errors:

  • TypeError: Occurs when you perform illegal operations with variables, such as attempting to execute undefined or null, access properties on undefined or null, or reassign a const variable.

TypeError happens at runtime.

  • SyntaxError: Happens when there are syntax errors in your code, such as extra parentheses or curly braces, improper dots, or illegal operations with let or const, like redefining a variable multiple times.

SyntaxError occurs during the compile time.

  • ReferenceError: This error arises when you try to access a variable that doesn't exist, and JavaScript can't find it. ReferenceError includes situations like accessing undeclared variables or trying to access variables before they are declared.

ReferenceError happens at runtime.

Undeclared vs. Undefined

Understanding the difference between undeclared and undefined is crucial in JavaScript:

  • Undefined: It signifies that a variable exists but currently has no assigned value. It might have never had a value or previously had a value but doesn't at the moment. It represents an empty state, where the value is missing.

  • Undeclared: An undeclared variable doesn't exist in any accessible scope. It's essentially unknown in your code, and trying to access it results in a ReferenceError.

Nested Scopes

JavaScript's lexical scopes can be envisioned as nested layers, similar to a multi-story building. JavaScript conducts a sequential search for variables, beginning from the current scope (the first floor) and progressively moving to outer scopes. This process mimics a linear search, akin to using an elevator to traverse floors within a building.

In this conceptual analogy:

  • The first floor represents the current scope, where a reference is made.
  • The top floor corresponds to the global scope. The search progresses one floor at a time, ensuring that variables are resolved in a structured and predictable manner. This methodology forms the foundation of JavaScript's lexical scope.

Arguments and Parameters in JavaScript

In JavaScript, functions play a crucial role in processing data and performing tasks. To understand how functions work, it's essential to grasp the concept of arguments and parameters, which are integral to how functions receive and process data.

Parameters: Receiving Data

Parameters are the placeholders or variables defined in a function's declaration. They act as targets for the data you pass to the function when you call it. Parameters specify what kind of data the function expects to work with and provide names for accessing that data within the function's scope.

Here's an example of a function with parameters:

function greet(name) {
  console.log(`Hello, ${name}!`);
}
Enter fullscreen mode Exit fullscreen mode

In this function, name is a parameter. It serves as a target for the data you pass when calling the greet function. For instance, if you call greet("Alice"), the parameter name will take on the value "Alice," and the function will greet Alice.

Arguments: Passing Data

Arguments are the actual data or values you provide when calling a function. These values are assigned to the function's parameters. Arguments are the source of data that the function works with.

Here's how you pass arguments to the greet function:

greet("Bob"); // "Bob" is the argument
greet("Carol"); // "Carol" is the argument
Enter fullscreen mode Exit fullscreen mode

In these examples, "Bob" and "Carol" are the arguments that get assigned to the name parameter within the greet function.

Parameters Create Identifiers

It's important to note that when you declare parameters in a function, they formally create identifiers within the function's scope. These identifiers allow the function to access and work with the data you pass as arguments.

In the greet function example:

function greet(name) {
  console.log(`Hello, ${name}!`);
}
Enter fullscreen mode Exit fullscreen mode

The name parameter is a formal identifier within the function's scope, allowing you to use it to reference the argument you pass when calling the function.

Conclusion

By delving into these fundamental aspects of lexical scope, you'll gain a deeper understanding of JavaScript and how it manages variables, errors, and scope. This knowledge is invaluable for writing efficient, optimized code and solving complex programming challenges.

Sources

Kyle Simpson's "You Don't Know JS"
MDN Web Docs - The Mozilla Developer Network (MDN)

Top comments (0)