JavaScript Execution – II
To read the part 1 of this post, click here.
Simple function expression
Let’s take this example:
// Example 1
1. var foo;
2. foo = function () {
3. var a = 10;
4. console.log (10);
5. }
6. foo ();
7. console.log ('Done');
I have created foo
variable before assigning a value to keep the execution simple. Let’s go through the two phase execution for this piece of code.
Phase 1:
- Line 1: New variable
foo
, allocate memory forfoo
. - Line 2: Skip [assignment].
- Line 3-5: Skip [function].
- Line 6. Skip [execution].
- Line 7: Skip [execution].
Phase 2:
- Line 1: Skip
- Line 2: Assignment, assign anonymous function to
foo
, since function in JavaScript is also treated as values, it is possible to assign the function to a variable. But this anonymous function keeps a reference to the content of the function which is stored in the heap [same as function statement]. Andfoo
now points to that reference location in the heap. - Line 3-5: Skip
- Line 6: Execution, invoke function
foo
. What’s functionfoo
here? We have not created any named functionfoo
, butfoo
indeed has a reference to the anonymous function [line 2]. Execute it in the same way, as we did earlier. - Line 7: Execution, invoke function
console.log
.
Functions with return value
// Example 2
1. function foo () {
2. return 'test';
3. }
4. var str = foo ();
5. console.log (str);
The function here returns a string test
and is assigned to a variable str
. Let’s see the execution:
Phase 1:
- Line 1: New variable
foo
of typefunction
, create a reference [as done earlier]. - Line 2-3: Skip [function].
- Line 4: New variable
str
, allocate memory forstr
. - Line 5: Skip [execution].
Phase 2:
- Line 1-3: Skip [function].
- Line 4: Assignment
- Assign the value
foo ()
to str, but what’sfoo ()
? - Execution, invoke function
foo
. - As discussed earlier, after the execution of a function, it needs to return to it’s previous location. It does so when it encounters the return statement. And this is the reason every function returns a value [if not mentioned, it returns
undefined
]. foo
in this case returns a value, which istest
.- Line 4 says, assign the value of
foo()
tostr
and invocation offoo
returnstest
, hencetest
is assigned tostr
and the execution pointer resumes to line 5.
- Assign the value
- Line 5: Execution, invoke function
console.log
An important point to note here is, each function is executed in its own scope and all the functions run for completion [not talking about generators of ES6 yet]. Which means, once the execution enters a function, it will resume its old line of code only when that function completes its execution. This completion is understood by the return value of the function.
How does the so called execution pointer knows where to return once it encounters the return statement inside a function?
During the execution phase [Phase 2], whenever it encounters a function invocation, while leaving the current line of execution [which technically is called context or scope], it creates a back-reference to the current context.
So, let’s say
- During the execution of
foo
, the execution pointer encounters another functionbar
. - It leaves the
foo
context, creates a back-reference from the context ofbar
to the context offoo
, something like this:foo <-- bar
. - When
bar
returns, it know where to go, i.e. back tofoo
.
Hoisting in function
If you were paying attention, you should know by now that function expressions cannot be hoisted while function statement can be.
The phase 2 of the JavaScript engine requires to execute or invoke the function. The execution requires a reference to the content of the function stored somewhere in the heap. But in case of function expression, it won’t find any.
Let’s see an example of a function statement which gets hoisted, followed by another of a function expression, which does not get hoisted.
// Example 3
1. foo ();
2. function foo () {
3. console.log ("Hoisted!");
4. }
Phase 1:
- Line 1: Skip [execution].
- Line 2: New variable
foo
of typefunction
, create a reference to the content of the function. - Line 3-4: Skip [function].
Phase 2:
- Line 1: Execution, invoke function
foo
. - Rest of the lines, like other examples.
// Example 4
1. bar ();
2. var bar = function () {
3. console.log ("Hoisted?");
4. }
Phase 1:
- Line 1: Skip [execution].
- Line 2: Here is the difference.
- New variable
bar
not of typefunction
, but it is like any other variable. - Allocate memory for
bar
.
- New variable
- Line 3-4: Skip [function].
Phase 2:
- Line 1: Execution, invoke function
bar
Would it be possible? We have seen earlier that if memory is allocated to a variable and if we try to access it, it gives the value undefined
.
Read more about undefined
here.
Line 1 in this case is asking to execute/invoke undefined, which is not a function. An execution requires a reference to the content of the function. Therefore, this code will result into error.
Uncaught TypeError: bar is not a function
Leave a Reply