In this series, we use JSVisualizer, a free browser-based tool, to understand classic algorithms by watching them run. Last time we looked at conditionals.
This time the topic is functions, and the star of the show is the Call Stack view.
Where beginners get stuck
This program adds the squares of two numbers. 3 * 3 + 4 * 4 is 25.
function square(x) {
return x * x;
}
function sumOfSquares(x, y) {
return square(x) + square(y);
}
let x = 10;
let answer = sumOfSquares(3, 4);
console.log(answer);
console.log(x);
What does the last line, console.log(x), print? People who've just met functions often wonder:
-
xshows up in three places. It's a parameter (a variable that receives a value passed to a function) ofsquare, a parameter ofsumOfSquares, and thelet xoutside. Are these the same thing or different things? -
Where does execution go when you call a function, and how does it get back? Partway through
square(x) + square(y), the program jumps into another function. How does it know where to pick up again? - If a function changes a value, does the change reach the outside?
JSVisualizer's Call Stack view shows you exactly this: each function call gets its own place for variables, which appears when the call starts and vanishes when it ends.
Seeing it in the Call Stack view
How to read it
Paste the code into JSVisualizer, press ▶ Run, and open the Call Stack tab. Then press ▷ Human (one "human-friendly" step) three times:
Each box is called a frame: a place for variables that belongs to one particular function call.
-
Global holds the variables outside any function, and it's always at the top.
square = ƒ squaremeans there's a function namedsquare, andanswer = —meansanswerdoesn't have a value yet. -
sumOfSquares(3, 4)is the frame for the call we just made. Its heading shows the values that were passed in. - The blue outline marks the frame that's running right now.
The sumOfSquares frame has x = 3, while Global has x = 10. Same name, but they live in different places: they're two separate variables.
Calls pile up
Press ▷ Human six more times (nine in total), and look at the whole screen:
- On the left, line 2 inside
squareis running, and the call that got us here,square(y), has a dashed purple underline. - On the right, a
square(4)frame has appeared under Global, pushingsumOfSquares(3, 4)down. New frames always appear right below Global.
Now look at x: it's 10 in Global, 4 in square(4), and 3 in sumOfSquares(3, 4). Three variables with the same name exist at the same time, with three different values.
Frames vanish
Press ▷ Human five more times (14 in total):
All the function frames are gone and only Global remains. answer now holds 25. The x = 3 and x = 4 we saw earlier disappeared with their frames, and Global's x is still 10. That's what the final console.log(x) prints.
What the frames reveal
1. Same name, different frame, different variable
A variable exists only in the frame where it was created. When square says x, it means the x in square's frame. The region of code where a variable can be used is called its scope.
This is why you can write a function without worrying about what variable names its callers happen to use.
2. The newest call finishes first
Step through and watch the frames:
- The
sumOfSquares(3, 4)frame appears - The
square(3)frame appears, then vanishes when it finishes - The
square(4)frame appears, then vanishes when it finishes - The
sumOfSquares(3, 4)frame vanishes
The most recent frame is always the first to go, just like taking plates off the top of a pile. A pile that works this way is called a stack, and since this one is made of function calls, it's the call stack.
Notice that the sumOfSquares frame stays put while square runs. That's how execution can pick up where it left off in square(x) + square(y).
3. Changing a parameter doesn't change the variable outside
What does this program print?
function addOne(n) {
n = n + 1;
return n;
}
let n = 10;
addOne(n);
console.log(n);
Not 11. Pausing inside addOne shows why:
The n in the addOne(10) frame has become 11, but Global's n is still 10. A function doesn't receive the variable itself. It receives a copy of its value. (Arrays and objects work a little differently; we'll get to that later in the series.)
When the function ends, its frame vanishes, and the 11 goes with it, because nothing used the value that return n handed back. Write n = addOne(n); and the returned 11 is stored in Global's n.
Try it yourself
Open the link below and press ▶ Run; the Call Stack view opens. Step forward with ▷ Human (the h key) and back with ◁ Human (H).
▶ Open in JSVisualizer (sum of squares, Call Stack view)
To change the code, press ✏ Edit. Before each run, predict how the frames will behave:
- Change
sumOfSquares(3, 4)tosumOfSquares(square(2), 4). Which frame appears first? - Add
console.log(y);at the very end. What happens, and why?
One more program to play with:
-
▶ Changing a parameter inside a function: change
addOne(n);ton = addOne(n);. When does Global'snchange?
Wrapping up
- Calling a function creates a frame, a place for variables that belongs to that call. When the call ends, the frame vanishes.
- Variables with the same name in different frames are different variables. The Call Stack view shows them side by side, with different values.
- The newest frame is the first to go. A function receives a copy of a value, so changing a parameter inside doesn't change the variable outside.
Next up: how expressions are evaluated. We'll use the Expr view to see the order in which a complicated expression like a + b * c > 10 && d gets worked out.





Top comments (0)