How Javascript works
Local Scope
In JavaScript, a local scope refers to the area within a program where a variable is declared and can be accessed. JavaScript has function-level scope, which means that variables declared inside a function are only accessible within that function.function exampleFunction() { // This variable 'localVariable' is within the local scope of the function. var localVariable = "I am a local variable"; console.log(localVariable); } exampleFunction(); // Output: I am a local variable // This will result in an error because 'localVariable' is not accessible outside the function. console.log(localVariable); // Error: localVariable is not defined
The let and const keywords allow for block-level scoping, which means variables declared with let and const are confined to the block (a set of statements surrounded by curly braces {}) in which they are defined, rather than just the function. This is in contrast to the var keyword, which is function-scoped.
function exampleFunction() { if (true) { let blockVariable = "I am a block-scoped variable"; console.log(blockVariable); } // This will result in an error because 'blockVariable' is not accessible here. console.log(blockVariable); // Error: blockVariable is not defined } exampleFunction(); // Output: I am a block-scoped variable
In this example, blockVariable is only accessible within the block where it is declared.
Global Scope
In JavaScript, the global scope refers to the outermost scope in a program, where variables and functions are accessible from any part of the code. When you declare a variable or a function outside of any block or function, it becomes part of the global scope.// Global variable var globalVar = "I am a global variable"; function myFunction() { // Accessing the global variable inside a function console.log(globalVar); } myFunction(); // Outputs: I am a global variable // Global function function globalFunction() { console.log("I am a global function"); } globalFunction(); // Outputs: I am a global function
In this example, globalVar and globalFunction are in the global scope. They can be accessed and used anywhere in the code, both inside and outside functions.
However, it's generally considered good practice to minimize the use of global variables to avoid unintended side effects and potential naming conflicts.Execution Context
In JavaScript, an execution context is an abstract concept that encompasses the environment in which JavaScript code is executed. Understanding execution contexts is crucial for comprehending how JavaScript code runs. There are two main types of execution contexts in JavaScript,
i) Global Execution Context
The global execution context is the default or outermost context.
It is created when the JavaScript code is executed, and it represents the global scope.
In a browser environment, the global object is the window object.
ii) Function Execution ContextEach time a function is invoked, a new function execution context is created.
It includes the function's local variables, parameters, and a reference to the outer (enclosing) execution context.
The context is popped off the stack when the function execution completes.
Each execution context has two phases -
Creation phase and Execution phase.Scope Chain
In JavaScript, the scope chain refers to the hierarchical structure of scopes that a program maintains during its execution. A scope in JavaScript is a region of code where a variable can be accessed or modified. Scopes can be nested within each other, creating a scope chain.When a variable is referenced in JavaScript, the interpreter searches for that variable in the current scope. If the variable is not found, it looks in the outer scope, continuing this process until the variable is found or the global scope is reached.
var globalVariable = 'I am global'; function outerFunction() { var outerVariable = 'I am outer'; function innerFunction() { var innerVariable = 'I am inner'; console.log(globalVariable); // Access global variable console.log(outerVariable); // Access variable from the outer function console.log(innerVariable); // Access variable from the current function } innerFunction(); } outerFunction();
Variable Shadowing
Variable shadowing in JavaScript occurs when a variable declared within a certain scope has the same name as a variable in an outer scope. This can lead to unexpected behavior, as the inner variable "shadows" or takes precedence over the outer variable within its scope.let x = 10; // Outer variable function example() { let x = 5; // Inner variable, shadows the outer variable within this function's scope console.log(x); // Prints 5, referring to the inner variable } example(); console.log(x); // Prints 10, referring to the outer variable
Hoisting
Hoisting in JavaScript is a behavior where variable and function declarations are moved to the top of their containing scope during the compilation phase, before the code is executed. This allows you to use variables and functions before they are declared in your code.
It's important to note that only the declarations are hoisted, not the initialization or assignments.i) Variable Hoisting
In the example, the declarationvar x;
is hoisted to the top, but the assignment (x = 5;
) is not hoisted. So, when you try to logx
before the assignment, it printsundefined
.console.log(x); // Output: undefined var x = 5; // if you use const then it will give referenceError console.log(x); // Output: 5
ii) Function Hoisting
In this example, the entire function declaration is hoisted to the top. Therefore, you can call the function before its actual declaration in the code.sayHello(); // Output: "Hello, World!" function sayHello() { console.log("Hello, World!"); }
However, there are some differences between function declarations and function expressions in terms of hoisting,
// Function Declaration hoistedFunction(); // Output: "I'm hoisted!" function hoistedFunction() { console.log("I'm hoisted!"); } // Function Expression nonHoistedFunction(); // Error: nonHoistedFunction is not a function var nonHoistedFunction = function() { console.log("I'm not hoisted!"); };
In the case of function expressions (where a function is assigned to a variable), only the variable declaration is hoisted, not the function assignment. Therefore, trying to call the function before the assignment results in an error.
It's important to be aware of hoisting in JavaScript to avoid unexpected behavior in your code. To improve code readability and avoid issues, it's often recommended to declare variables at the top of their scope and define functions before they are called.How Javascript Engine Works
JavaScript code gets executed in a multi-step process, involving various phases and components. Here is an overview of the typical process,
i) Tokenization/Lexical Analysis
The JavaScript engine breaks the source code into meaningful chunks called tokens. This process is known as tokenization or lexical analysis.ii) Parsing/Abstract Syntax Tree (AST) Generation
The engine then parses the tokens and generates an Abstract Syntax Tree (AST). The AST represents the syntactic structure of the code, showing how different elements are related.iii) Execution Context Creation
The engine creates an initial execution context, known as the global execution context, for the entire script. This context includes the global object (e.g.,window
in a browser), thethis
reference (pointing to the global object in non-strict mode), the outer environment (null for the global context), and a reference to the global variable object.iv) Hoisting
During the creation phase of the execution context, variable and function declarations are hoisted. Variables are initialised toundefined
, and function declarations are fully hoisted. This allows you to use functions and variables before their actual declarations in the code.v) Execution and Context Stack (Call Stack)
The engine starts executing the code line by line. As it encounters function calls, new execution contexts are created and pushed onto the call stack. Each function's context includes its own set of local variables, parameters, and a reference to its outer environment.vi) Scope Chain
JavaScript uses a scope chain to resolve variable references. Each function's scope includes not only its own variables but also variables from its outer (enclosing) scopes. The scope chain is used to find and access variables during execution.vii) Execution and Variable Assignment
The engine executes the code, assigning values to variables and performing operations. Function calls create new execution contexts, and the call stack reflects the hierarchy of currently executing functions.viii) Asynchronous Callbacks and Event Loop
Asynchronous operations, such as timers, AJAX requests, or user interactions, are handled using callback functions. These callbacks are placed in the callback queue when the associated events occur. The event loop constantly checks the call stack and the callback queue, moving callbacks to the call stack when it is empty.ix) Memory Management and Garbage Collection
The engine manages memory allocation and de-allocation, including garbage collection to free up memory that is no longer in use.
Global Execution Context
In JavaScript, the global execution context is the outermost or top-level context in the execution stack. When a JavaScript program is run, the global execution context is created first.
It consists of two main components,
i) Global Object
In a browser environment, the global object iswindow
. In Node.js, it isglobal
. The global object is a container for various properties and methods that are accessible globally. For example, functions likesetTimeout
andconsole.log
are properties of the global object.
ii) Global Scope
The global scope encompasses variables and functions that are defined outside of any function or block. Variables declared in the global scope are accessible throughout the entire program.
iii) Additionally, the global execution context sets up the following:this
: In the global context,this
refers to the global object (window
in browsers,global
in Node.js).When a JavaScript program starts running, the global execution context is created, and code is executed line by line. Functions and variables declared in the global scope are available throughout the entire program. Other execution contexts are created when functions are called, and they have their own scope chains and local variables.
Call Stack
The call stack is a critical concept in JavaScript's runtime environment, providing a mechanism for managing the flow of execution in a program. It keeps track of the currently executing functions and their contexts during the execution of a script.
In JavaScript, when a function is called, a new execution context for that function is created, including information such as local variables and the current position in the code. These execution contexts are organized in a stack-like structure known as the call stack.
Here's a simplified explanation of how the call stack works:
i) Function Call: When a function is called, a new execution context is created for that function, and it is pushed onto the call stack.
ii) Execution: The code inside the function is executed line by line within its execution context.
iii) Return: When the function completes its execution or encounters areturn
statement, the execution context is popped off the call stack.
iv) Control Flow: The control is then transferred back to the previous execution context on top of the stack, and the program continues its execution from where it left off.
This process repeats as functions are called and return, forming a stack of execution contexts.Callback Queue
In JavaScript, the callback queue is closely associated with the event loop and is a key concept in handling asynchronous operations. The callback queue holds functions (callbacks) that are ready to be executed, but they are waiting for the execution stack to be empty.
Here's a brief overview of how the callback queue works in conjunction with the event loop,
i) Execution Stack
The execution stack is where the JavaScript engine keeps track of the functions that are currently being executed. When a function is called, its execution context is pushed onto the stack, and when the function completes, its context is popped off the stack.
ii) Callback Queue
The callback queue holds functions (callbacks) that are scheduled to be executed as a result of asynchronous events. These events could be I/O operations, timer events (such assetTimeout
orsetInterval
), or other asynchronous operations.
iii) Event Loop
The event loop continuously checks the state of the call stack and the callback queue. If the call stack is empty, and there are functions in the callback queue, the event loop will move a function from the callback queue to the call stack for execution.Temporal Dead Zone
In JavaScript, the term "temporal dead zone" (TDZ) refers to a specific behavior related to the hoisting of variables declared with thelet
andconst
keywords. The temporal dead zone is the period between the start of the execution of a scope (such as a function or a block) and the point where a variable is declared. During this period, if you try to access the variable, aReferenceError
will be thrown.
Here's an example to illustrate the temporal dead zone:console.log(variable); // ReferenceError: variable is not defined let variable = 42; console.log(variable); // 42
In this example, even though the
console.log(variable)
statement appears before the variable declaration, it throws aReferenceError
because the variable is still in the temporal dead zone at that point. Only after thelet variable = 42;
declaration statement is encountered during the execution, the variable becomes accessible.
It's important to note that the temporal dead zone only applies to variables declared withlet
andconst
, not to variables declared withvar
.https://cabulous.medium.com/how-v8-javascript-engine-works-5393832d80a7