Scope in JavaScript
How does JavaScript know what variable can be looked up? Scope. If you don't understand how JavaScript running, it's hard to understand scope. In this post, I will take about Execution Context , scope callback, and closure.
How is javascript Running?
Javascript goes through the code line-by-line and runs/ executes each line which is known as the thread
of execution. It needs somewhere to save variables like string and array for later use; this place is called memory
.
Note; thread and memory also called Execution context
Thread
Javascript is single-threaded, can only do one thing simultaneously, only one statement/one line is executed. This thread is synchronous execution, start from the first line of function to the last line. for asynchronous, it has to ask browser API for help(fetch, timeout. etc.)
Memory
Memory is known as variable environment. Declare/register/define, take the label, and save code there.
Call stack
call stack helps javaScript track which function is currently running(the thread is running), and where to it's after return from the current function. When a function is running, add it to calls stack. finish execute a function, removes from call stack, retune to lower stack the top of the call stack is the function we’re currently running. The bottom stack is always global. Global never pop out even if there is no more code to execute, but the application is still running. Image user clicks a button. The application has to show content base on some state, that needs a thread waiting for user operation.
Scope
Scoping answers where can we access a certain variable or function, Javascript scope is lexical scoping. What do you mean by lexical? Well. Lexical
is relating to the words or vocabulary of a language. You can think the variable is a word of some language; a dictionary includes all of those words. Lexical is about the position of definition within our code. The position of a word is fixed in dictionary; you always look up a word in same page same line.
Lexical scoping
means Where you define your functions determines what data it has access to when you call it. Those data never changed, because scopes/identifiers are determined at compile time.Our lexical scope is what determines our available variables at function execution, not where our function is called .
Compile code
the lexically scoped language, which JavaScript is, mean scopes/identifiers is determined at compile time. (know where and how all identifiers are declared) It is used at run time, but it is determined at compile time. And why that matters is that it allows the engine to much more efficiently optimize because everything is known and it's fixed. Nothing during the run time can determine
let look at this example:
var num = 1;
function calc() {
var num2 = 2;
var result = num + num2;
console.log(result)
}
calc()
JavaScript compiler finds out all declaration/define at compile-time and create scope; each function has its scope. the most outside one are called global scope
Run my code
compiler outputs is executable code, is a plan for that scope what variable and function(we create another scope in outer scope) will have. When engine runs is when it all actually comes into existence, value in scope plan became variable in memory, code is running in a thread.
initializes it num to undefined, then assign value to num identifier 1 It has a reference to function calc, which is attached to another scope, make this scope live: add value in scope to memory, move thread in this function. Fist Javascript engine initializes to undefined and assigns value 2; then engine initializes result to undefined, find out num in global memory; num2 in function memory, assign value 3 to resule. After function return, there is no code to execute. Our thread is empty
Hoisting
Is javascript moving all declarations to the top of the scope? No. Scopes are created at compile time before Executing code. JavaScript engine knows what variable is available; just look up the memory. If no value is assigned to an identifier, you get back undefined. that behavior as if the variable declaration is move to the top of the scope
Closure
definition from YDKJS:
Observational: closure is a function instance remembering its outer variables even as that function is passed to and invoked in other scopes.
Implementational: closure is a function instance and its scope environment preserved in-place while any references to it are passed around and invoked from other scopes.
what do you mean by remembering or preserved? How can javascript do that? every function has a [[Scope]]
property to keep track of reference to outer memory. we call a function it will always look first in its immediate local memory (variable environment), and then in the [[scope]]
property
note: function's [[scope]]
maintain outer’s live local memory, not include in local memory of function itself
example
function out(){
let count = 0;
function inf() {
let width = 1;
count += 1
}
console.dir(inf)
return inf
}
Console.dir()
displays a list of the properties of inf function, [[Scopes]]
has a list of outer scope.