JavaScript: how it works
how does this language work?
Last updated
how does this language work?
Last updated
Before going through the following, this article covers
the JavaScript Engine
the interpreter that compiles JS code into machine language
the JavaScript Runtime
the JS Engine user and provider of additional functionalities
Let's try to understand how JavaScript works with the help of some very nicely written articles from JavaScript Tutorial:
What actually happens when we declare variables and functions, as well as call them?
When we declare a variable as such:
...the "JavaScript engine" uses an execution context made up of two phases in order to:
declare the existence of the variable x
(the creation phase)
so this variable actually begins with a value of undefined
setup a space in memory (the memory heap) to store this variable
finally assign x
a value (in this case, 10
) (the execution phase)
(...the article also explains a smaller-scale version of execution contexts within local scopes)
When we have a lot of execution contexts, leap-frogging from here to there and everywhere, what gets called first? Well, the JavaScript engine uses the call stack to ensure priority:
When we create a new local execution context, we add ("push") it to the call stack
When we add another context on top of that, we stack that on top of the call stack
When we finish with the latest context, we remove ("pop") it from the call stack
The last context on the stack gets popped off first, until all contexts get popped off
"Last in, first out" (LIFO)
If everything is all so LIFO in the call stack, then JavaScript can do only one thing at a time! So, how do we get it to multi-task? Some functions might take a very long time to execute (think of a file download) and this can cause blockage! Just how do we make things more efficient?
By using special functions called callback functions, we can
let the web browser make use of other components (e.g. the Web API and callback queue)
allow some "slower" functions to by-pass the call stack of the JavaScript runtime!
When we declare global variables, all variables act as though they appeared (hoisted) at the top of the code:
Functions then get hoisted above the variables, so the code behaves in a rearranged fashion as such:
function declarations
variable declarations
function calls
So, where does a variable live?
A variable not inside any function
exists in the global scope
We can access this variable from anywhere in the script
On a browser, this scope is the entire browser window
A variable inside a function
will exist only inside its curly braces
We cannot use this variable from outside of the braces!
We can only take its value outside if
something in the global scope calls the function
the function includes the value in its return
statement
Block scoping occurs when
We declare a variable with the let
keyword inside curly braces
this includes if
blocks
If a variable appears in the global scope and then appears in a local scope, then what happens?
We would expect the first console.log
to return 1
the return x
there refers to the x inside the local scope of k()
the value of x
inside the brackets!
We would then expect the second console.log
to return 2
the console has no idea that the x
inside of k()
exists
it has no memory of the first log
only the global x
exists!
So, as soon as another x
gets declared inside a local scope, a variable such as x
takes on a new context!
As discussed in the article, weird stuff happens when we assign a value to a variable that we never declared (or "declare without a keyword"):
In this case, the JavaScript engine:
looks for the counter
variable in the local scope
finds none
looks for the counter
variable in the global scope
finds none
creates a counter
variable in the global scope (!!!)
In order to avoid this "weird problem" of memory leaks into the global scope, we would append this even weirder string literal at the top of the code, called use strict
:
Now, another weird thing:
A function has a closure when it can access a variable outside of its scope - in this example, cat
is available via this
in printInfo
:
Yet, a nested function within a parent function won't have access to the variables accessible by the parent function! Instead, the nested function gets bound to the global scope (!!)
The article then explains a way to mitigate by using a helper variable:
Of course, the article also lets us know the existence of three other built-in JavaScript functions: call()
, apply()
and bind()
that make the code a little cleaner!
So, in the above, call()
, apply()
and bind()
all do the same thing and print out the same result but:
apply(thisArg, [var1, var2, ..., varn])
can take on more variables within an array as the second argument
n
is however many arguments the function
bind(thisArg, var1, var2, ..., varn)
can take on multiple variables in multiple arguments
allows the changing of what this
references
allows storage in another variable for use
call(thisArg, var1, var2, ..., varn)
takes on multiple variables in multiple arguments
basically calledFunction()
becomes calledFunction.call(this)
with the this
In all three functions, the first argument consists of what this
means, while the other arguments map onto the parametric signature for the nested function!
Let's look at this example for clarity:
Such a paradigm allows us to create functions in more abstract ways for more robust code re-use!
So those concepts form some of the nuances of how JavaScript works behind the browser window!