⬅ Back

FUNCTIONS (PART 2) IN JAVASCRIPT

This note explains the arguments pseudo-array, default parameters, function expressions, scope, scope chain, and the call stack in simple language.

These topics are very important because they explain how functions really behave in JavaScript.

1. The arguments pseudo-array

When you call a regular function, JavaScript automatically creates a special variable inside it:

arguments

This variable contains all arguments that were passed to the function.

Example

function sum(a, b) {
  console.log(arguments);
  return a + b;
}

sum(2, 5);

When sum(2, 5) runs, arguments contains 2 and 5.

Diagram 1. What arguments contains

Function call:
sum(2, 5)

Inside the function:
arguments
│
├─ arguments[0] → 2
├─ arguments[1] → 5
└─ length       → 2

Explanation

Even if you already have named parameters like a and b, JavaScript still gives you arguments with all passed values.

2. Why it is called a pseudo-array

arguments looks like an array, but it is not a real array.

That is why it is called a pseudo-array or array-like object. It has some array behavior, but not all.

It can do these things

But it cannot do these things directly

Diagram 2. arguments vs real array

arguments
│
├─ has indexes        → yes
├─ has length         → yes
├─ works in loops     → yes
└─ has array methods  → no

Real array
│
├─ has indexes        → yes
├─ has length         → yes
├─ works in loops     → yes
└─ has array methods  → yes

Explanation

This is the most important thing to remember: arguments is array-like, but not a real array.

3. Accessing values inside arguments

You can read its values by index.

function showArgs() {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments.length);
}

showArgs("apple", "banana");

Output

apple
banana
2

Diagram 3. Access by index

showArgs("apple", "banana")

arguments[0] → "apple"
arguments[1] → "banana"
arguments.length → 2

Explanation

It works very much like an array for basic reading.

4. Iterating over arguments

Because arguments can be iterated, you can loop through all passed values.

function multiply() {
  let total = 1;

  for (const arg of arguments) {
    total *= arg;
  }

  return total;
}

console.log(multiply(1, 2, 3));       // 6
console.log(multiply(1, 2, 3, 4));    // 24
console.log(multiply(1, 2, 3, 4, 5)); // 120

Diagram 4. How multiply() works with arguments

Call:
multiply(1, 2, 3, 4)

arguments:
[1, 2, 3, 4]   ← pseudo-array idea

total = 1
↓
1 * 1 = 1
↓
1 * 2 = 2
↓
2 * 3 = 6
↓
6 * 4 = 24
↓
return 24

Explanation

This is useful when you do not know in advance how many arguments the function will receive.

5. Converting arguments to a real array

If you want to use real array methods, convert arguments into a real array.

Array.from(arguments)

Example

function foo() {
  const args = Array.from(arguments);
  return args.join("-");
}

console.log(foo(1, 2, 3)); // "1-2-3"

Diagram 5. Converting arguments

arguments
   ↓ Array.from(arguments)
real array
   ↓
now array methods work

Explanation

After conversion, you can use methods like join(), slice(), push(), and other array methods.

6. Step by step for Array.from(arguments)

function foo() {
  const args = Array.from(arguments);
  return args.join("-");
}

console.log(foo(1, 2, 3));

Diagram 6. Full flow

foo(1, 2, 3)
   ↓
arguments = {0: 1, 1: 2, 2: 3, length: 3}
   ↓
Array.from(arguments)
   ↓
[1, 2, 3]
   ↓
join("-")
   ↓
"1-2-3"

Explanation

This shows why conversion is useful.

7. Default parameters

A function parameter can have a default value. That value is used when no argument is passed for that parameter.

function greet(username = "Guest") {
  console.log(`Hello, ${username}!`);
}

greet("Jacob"); // "Hello, Jacob!"
greet();        // "Hello, Guest!"

Diagram 7. Default parameter logic

greet("Jacob")
username = "Jacob"

greet()
username = "Guest"

Explanation

If an argument exists, it replaces the default value. If no argument is passed, the default value is used.

8. Why default parameters are useful

Default parameters make functions more flexible.

They help you avoid writing extra checks like “Was a value passed?” or “Should I use some basic value instead?”

Diagram 8. Why defaults help

Without default:
need extra check

With default:
function already knows fallback value

Explanation

This makes code shorter and easier to read.

9. Example with more than one parameter

function count(from, to, step = 1) {
  console.log(`from: ${from}, to: ${to}, step: ${step}`);

  for (let i = from; i <= to; i += step) {
    // ...
  }
}

count(1, 15, 4); // "from: 1, to: 15, step: 4"
count(1, 15);    // "from: 1, to: 15, step: 1"

Diagram 9. How step = 1 works

count(1, 15, 4)
step = 4

count(1, 15)
step = 1   ← default value

Explanation

If the third argument is missing, JavaScript uses 1.

10. Important idea about default parameters

Default values are used only when the argument is missing.

function greet(name = "Guest") {
  console.log(name);
}

greet(); // "Guest"

Diagram 10. Default parameter rule

Argument passed?
│
├─ yes → use passed value
└─ no  → use default value

Explanation

This is the simple rule for default parameters.

11. Function declaration

You already know this syntax:

function multiply(x, y, z) {
  console.log(x * y * z);
}

This is called a function declaration.

Diagram 11. Function declaration structure

function multiply(x, y, z) {
  console.log(x * y * z);
}

1. function keyword
2. function name
3. parameters
4. function body

Explanation

This is the classic way to create a function.

12. Function expression

A function expression is when a function is stored inside a variable.

const multiply = function (x, y, z) {
  console.log(x * y * z);
};

Diagram 12. Function expression structure

const multiply = function (x, y, z) {
  console.log(x * y * z);
};

1. const variable
2. variable name = multiply
3. function is the value

Explanation

This is another valid way to create functions in JavaScript.

13. Function declaration vs function expression

These two styles look similar, but behave differently.

Function declaration

function multiply(x, y, z) {
  console.log(x * y * z);
}

Function expression

const multiply = function (x, y, z) {
  console.log(x * y * z);
};

Diagram 13. Main difference

Function declaration
→ can be called before its code appears

Function expression
→ can only be called after variable creation

Explanation

This is one of the most important differences.

14. Calling a function expression too early

This does not work:

multiply(1, 2, 3); // Error

const multiply = function (x, y, z) {
  console.log(x * y * z);
};

This causes an error because the variable does not exist yet at the moment of the first call.

Diagram 14. Why early call fails

Step 1:
try to run multiply(1, 2, 3)

Step 2:
JavaScript has not created const multiply yet

Result:
Error

Explanation

A function expression is stored in a variable, so the variable must be created first.

15. Calling a function expression after creation

This works:

const multiply = function (x, y, z) {
  console.log(x * y * z);
};

multiply(4, 5, 6); // works

Diagram 15. Correct order for function expression

1. Create variable multiply
2. Store function in it
3. Call multiply()

Explanation

You must always call a function expression after the place where it is created.

16. Function declarations can be called earlier

This works:

multiply(1, 2, 3);

function multiply(x, y, z) {
  console.log(x * y * z);
}

multiply(4, 5, 6);

Diagram 16. Function declaration calling order

Call multiply()
↓
JavaScript already knows function declaration
↓
works

Explanation

Function declarations behave differently from function expressions in this way.

17. Which style should you use?

Both styles are valid. The important thing is consistency.

Diagram 17. Best practice idea

Project style
│
├─ use function declarations consistently
or
└─ use function expressions consistently

Explanation

Mixing too many styles can make code harder to read.

18. What is scope?

Scope decides where variables are available in code.

A variable can be used if it is in the current scope or in an outer scope.

Diagram 18. What scope means

Scope = visibility area of a variable

Explanation

Scope answers this question: “Where can I use this variable?”

19. Global scope

A variable declared outside functions and blocks is in the global scope.

const globalValue = 10;

console.log(globalValue); // 10

function foo() {
  console.log(globalValue); // 10
}

for (let i = 0; i < 5; i += 1) {
  console.log(globalValue); // 10

  if (i === 2) {
    console.log(globalValue); // 10
  }
}

Diagram 19. Global variable visibility

Global scope
│
└─ globalValue = 10

Available:
- outside all blocks
- inside function foo()
- inside for block
- inside if block

Explanation

Global variables can be used in many places after they are declared.

20. Block scope

Variables declared inside blocks {} belong only to that block.

Examples of block scope include if, for, and functions.

function foo() {
  const a = 20;
  console.log(a); // 20

  for (let i = 0; i < 5; i += 1) {
    console.log(a); // 20

    if (i === 2) {
      console.log(a); // 20
    }
  }
}

console.log(a); // Error

Diagram 20. Local variable in function scope

function foo() {
  const a = 20;
}

Inside foo():
a exists

Outside foo():
a does not exist

Explanation

The variable a is local to the function. Outside the function, JavaScript cannot see it.

21. Scope chain

Scopes form a hierarchy. This is called the scope chain.

A child scope can access variables from its parent scope. But a parent scope cannot access variables from inner child scopes.

Diagram 21. Scope chain as levels

Global
│
└─ Function scope
   │
   └─ for block
      │
      └─ if block

Explanation

Code can look outward to parent scopes, but not inward to child scopes.

22. House and rooms idea

You can imagine scope like a house with rooms.

Variables in one room stay inside that room.

Diagram 22. House model of scope

House = Global scope

Room A = function
Room B = if block
Room C = for block

Explanation

If a variable is in Room B, you can use it inside Room B. But outside that room, it is unavailable.

23. Nested block scope example

for (let i = 0; i < 5; i += 1) {
  const a = 20;
  console.log(a); // 20

  if (i === 2) {
    const b = 30;
    console.log(a); // 20
    console.log(b); // 30
  }

  if (i === 3) {
    console.log(a); // 20
    console.log(b); // Error
  }
}

Diagram 23. Nested block visibility

for block
│
├─ a exists
│
└─ if (i === 2) block
   └─ b exists

Outside that if block:
b does not exist

Explanation

b only lives inside the block where it was created.

24. How JavaScript looks for variables

When JavaScript sees a variable name, it searches step by step:

  1. current scope
  2. parent scope
  3. next outer scope
  4. global scope

If it still cannot find the variable, it throws an error.

Diagram 24. Variable lookup process

Need variable?
↓
Look in current scope
↓
Not found?
↓
Look in outer scope
↓
Not found?
↓
Look in global scope
↓
Still not found?
↓
Error

Explanation

This is the basic rule of the scope chain.

25. Example with global name collision

{
  const name = "Aaron";
  console.log(name); // "Aaron"
}

console.log(name); // global variable name

Diagram 25. Same variable name in different scopes

Inside block:
name = "Aaron"

Outside block:
name = global name

Explanation

A local variable can hide another variable with the same name from an outer scope. This is why naming matters.

26. What is the call stack?

When functions call other functions, JavaScript needs a way to remember which function is running now, which function called it, and where to return after a function finishes.

That mechanism is the call stack.

Diagram 26. What the call stack does

Call stack
│
├─ remembers active function calls
├─ tracks current execution order
└─ knows where to return next

Explanation

Without the call stack, JavaScript would not know how to control nested function calls.

27. JavaScript is single-threaded

JavaScript runs one instruction at a time.

Diagram 27. Single-threaded idea

JavaScript
↓
one instruction at a time
↓
current function pauses while inner function runs

Explanation

This is why function calls happen in a strict order.

28. Example with nested function calls

function fnA() {
  console.log("Log inside fnA function before calling fnB");
  fnB();
  console.log("Log inside fnA function after fnB call");
}

function fnB() {
  console.log("Log inside fnB function");
}

console.log("Log before calling fnA");
fnA();
console.log("Log after calling fnA");

Output

Log before calling fnA
Log inside fnA function before calling fnB
Log inside fnB function
Log inside fnA function after fnB call
Log after calling fnA

Diagram 28. Order of execution with fnA and fnB

1. "Log before calling fnA"
2. call fnA()
3. inside fnA → log before fnB
4. call fnB()
5. inside fnB → log
6. fnB ends
7. return to fnA
8. fnA logs after fnB
9. fnA ends
10. "Log after calling fnA"

Explanation

The outer function pauses while the inner function runs. After the inner function finishes, JavaScript returns to the paused place.

29. Stack idea: LIFO

A stack works using the rule Last In, First Out (LIFO).

That means the last thing added is the first thing removed.

Diagram 29. Stack model

Top of stack
│
├─ newest item
├─ older item
└─ oldest item

Explanation

Imagine a stack of plates. You put new plates on top, and you also take plates from the top.

30. Call stack and function calls

When a function is called:

  1. JavaScript adds it to the call stack
  2. executes it
  3. if it calls another function, that new function goes on top
  4. when a function finishes, it is removed
  5. execution returns to the previous function

Diagram 30. Call stack rules

Function called
↓
push to stack
↓
execute
↓
finished?
↓
remove from stack
↓
return to previous call

Explanation

This is how JavaScript keeps order in nested function calls.

31. Example with foo, bar, and baz

function bar() {
  console.log("bar");
}

function baz() {
  console.log("baz");
}

function foo() {
  console.log("foo");
  bar();
  baz();
}

foo();

Diagram 31. Step-by-step call stack

Step 1:
Call foo()
Stack:
[ foo ]

Step 2:
Inside foo() → call bar()
Stack:
[ foo, bar ]

Step 3:
bar() ends
Stack:
[ foo ]

Step 4:
Inside foo() → call baz()
Stack:
[ foo, baz ]

Step 5:
baz() ends
Stack:
[ foo ]

Step 6:
foo() ends
Stack:
[ ]

Explanation

Each new function call goes on top of the stack. When it finishes, it is removed.

32. What is a stack frame?

Each function call creates a stack frame.

A stack frame stores information like function name, where it was called, and internal execution data.

Diagram 32. Stack frame idea

One function call
↓
one stack frame

Stack frame stores:
- function info
- call location
- execution state

Explanation

You do not usually work with stack frames directly, but they are part of how the call stack works.

33. Why the call stack matters

The call stack helps JavaScript keep order, return to the correct place, manage nested function calls, and report useful errors.

Diagram 33. Why call stack is useful

Call stack
│
├─ controls order
├─ remembers paused functions
├─ allows return to correct place
└─ helps detect stack overflow

Explanation

This is why the call stack is a core JavaScript mechanism.

34. Stack overflow

The call stack is not infinite. It has limited memory.

If too many function calls are added without stopping, JavaScript throws this error:

Uncaught RangeError: Maximum call stack size exceeded

Diagram 34. Stack overflow idea

Function call
↓
another function call
↓
another function call
↓
another function call
↓
stack grows too much
↓
RangeError

Explanation

This usually happens when function calls never stop.

35. Why stack overflow can happen

A common reason is endless recursion. That means a function keeps calling itself forever.

Example of bad recursion

function foo() {
  foo();
}

foo();

This never stops, so the stack keeps growing until it crashes.

Diagram 35. Endless recursion

foo()
↓
foo()
↓
foo()
↓
foo()
↓
... never stops
↓
stack overflow

Explanation

Every new call adds another frame to the stack. If there is no stopping condition, memory runs out.

36. Common beginner mistakes

Mistake 1. Thinking arguments is a real array

Wrong idea:

arguments.join("-")

This may fail because arguments is not a true array.

Better:

const args = Array.from(arguments);
args.join("-");

Mistake 2. Forgetting that default values are used only when argument is missing

If an argument is passed, the default is not used.

Mistake 3. Calling a function expression before creation

multiply(1, 2);

const multiply = function (x, y) {
  return x * y;
};

Mistake 4. Trying to use local variables outside their scope

function test() {
  const a = 10;
}

console.log(a); // Error

Mistake 5. Thinking parent scope can access child variables

Outer code cannot access variables declared only inside inner blocks.

Diagram 36. Common mistakes summary

1. arguments is not a real array
2. default parameters work only when argument is missing
3. function expressions must be called after creation
4. local variables stay inside their scope
5. child scope can see parent scope, not the other way around

Explanation

These are very common beginner problems in this topic.

37. Quick summary

arguments

A special pseudo-array with all passed arguments.

Pseudo-array

Array-like value with indexes and length, but without normal array methods.

Array.from(arguments)

Converts arguments into a real array.

Default parameters

Parameter values used when no argument is passed.

Function expression

A function stored in a variable.

Function declaration

A function declared with the function keyword directly.

Scope

The area where a variable is available.

Scope chain

A hierarchy where inner scopes can access outer scopes.

Global scope

Variables declared outside all blocks and functions.

Block scope

Variables declared inside {} and available only there.

Call stack

The structure that tracks active function calls.

Stack overflow

An error caused by too many unfinished function calls.

Diagram 37. Final map of Functions Part 2

Functions (Part 2)
│
├─ arguments
│  ├─ pseudo-array
│  └─ Array.from()
│
├─ default parameters
│
├─ function expression
│
├─ scope
│  ├─ global
│  ├─ local
│  └─ scope chain
│
└─ call stack
   ├─ stack frames
   └─ stack overflow

Explanation

This is the complete picture of the topic.

38. Revision block

1. arguments contains all passed arguments
2. arguments is not a real array
3. Array.from(arguments) creates a real array
4. Default parameters are used when an argument is missing
5. A function expression is a function stored in a variable
6. Function expressions must be called after they are created
7. Scope controls where variables are visible
8. Inner scopes can access outer scopes
9. Outer scopes cannot access inner variables
10. The call stack tracks active function calls
11. JavaScript uses Last-In-First-Out order in the call stack
12. Endless function calls can cause stack overflow

39. Final conclusion

This topic explains some of the deeper behavior of functions in JavaScript.

If you understand how arguments works, why it is a pseudo-array, how default parameters work, how function expressions differ from declarations, how scope and scope chain work, and how the call stack works, then your understanding of JavaScript functions becomes much stronger.

These ideas are very important for full stack development because they appear in reusable function design, parameter handling, nested logic, debugging, recursion, and real program execution flow.

⬅ Back