The Javascript runtime in the browser is comprised of several components:
The Javascript Engine - includes the heap and call stack
The WebAPI - Functionality that the browser reveals to the engine to use (timers, DOM etc.)
The Callback Queue - a list of executable code waiting for the stack to clear, to then push an event triggered action to execute.
The Event Loop - the mechanism that tracks and checks for insertion of executable code from the callback queue into the engine for running.
The call stack is the component of the JS engine that actually executes the blocks of codes, that are converted using JIT compilation into machine code.
It initializes a global execution context under which a variable environment is created including all top level declared and defined variables and functions.
For every function or block being called, the engine assigns a discrete execution context that inherits all upper execution context environments (meaning, all data defined and declared in levels above the function are in the scope of this function).
The engine's stack is also responsible for layering (adding new execution contexts as function are being called), and popping (clearing execution contexts when functions are terminating and called back).
As mentioned before, scope works as follows:
variables and functions defined and declared at the top level are part of the global execution context and are seen and available everywhere in the code.
Variables and functions that are defined inside other functions or blocks can use any of their inside declared variables/functions and also those declared in their containing function/block (but not in them)
In essence, scope works from the bottom up (inner parts see their own data as well as the outer parts in which they are contained).
This mechanism is called lexical scoping.
An important detail to be aware of, is that variables declared in function contained blocks (conditionals and loop structures inside a function), declared with the keywords let and const are scoped to the block where they are declared, whilst those declared with var get scoped to the closest container function.