Skip to main content

Chapter 4 - Functions (JavaScript)

Here's a JavaScript-flavoured version of the same concepts, with small JS examples for each idea.


Defining and calling functions

A function is a mini-program defined with function (or as an arrow function) and run later by calling its name with ().

Example:

function hello() {
console.log("Good morning!");
console.log("Good afternoon!");
console.log("Good evening!");
}

hello();
hello();

Arguments and parameters

Parameters are variables in the function definition; arguments are the actual values you pass in when calling.

Example:

function sayHelloTo(name) {  // name is a parameter
console.log("Good morning,", name);
}

sayHelloTo("Alice"); // "Alice" is an argument
sayHelloTo("Bob");

After the function returns, name no longer exists outside the function.


Return values and return

A function call evaluates to its return value, which you specify with return. If no return is used, the function returns undefined.

Example:

function square(x) {
return x * x;
}

const result = square(5); // 25
console.log(result);
console.log(square(3) + square(4)); // 25

Magic 8-ball style function

You can use return plus Math.random() to produce different answers depending on a number.

Example:

function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}

function getAnswer(n) {
if (n === 1) {
return "It is certain";
} else if (n === 2) {
return "Ask again later";
} else {
return "Very doubtful";
}
}

console.log(getAnswer(randInt(1, 3)));

undefined and null (equivalent of None)

undefined represents "no value returned." Functions with no explicit return return undefined. JavaScript also has null for explicitly "no value."

Example:

const spam = console.log("Hello!");  // prints Hello!
console.log(spam === undefined); // true

Also, return by itself returns undefined:

function doNothing() {
return;
}

console.log(doNothing() === undefined); // true

Named (keyword) parameters in built-ins

JavaScript doesn't have keyword arguments like Python, but you can achieve similar effects. For console.log, you control output differently:

No-newline output (end=""):

// Node.js
process.stdout.write("Hello");
process.stdout.write("World\n"); // HelloWorld

Custom separator (sep=","):

console.log(["cats", "dogs", "mice"].join(","));
// cats,dogs,mice

The call stack

JavaScript tracks where to return after each function call using a call stack. Each call adds a frame; returning pops that frame and resumes after the call.

Example:

function c() {
console.log("c() starts");
console.log("c() returns");
}

function b() {
console.log("b() starts");
c();
console.log("b() returns");
}

function a() {
console.log("a() starts");
b();
console.log("a() returns");
}

a();

You'll see the order: a() -> b() -> c() -> back out again.


Local vs global scope

Variables created inside functions with let/const live in a local scope; variables created at the top level live in the global scope. Local variables are not visible outside their function.

Example (global can't see local):

function spam() {
const eggs = "local eggs";
}

spam();
// console.log(eggs); // ReferenceError: eggs is not defined

Separate local scopes

Each function call has its own local scope, and different functions' locals cannot see one another's variables.

Example:

function spam() {
const eggs = "spam local";
bacon();
console.log("spam sees:", eggs);
}

function bacon() {
const eggs = "bacon local";
console.log("bacon sees:", eggs);
}

spam();

Here each eggs is separate.


Local scope can read globals

Code inside a function can read global variables (as long as you don't declare a local with the same name).

Example:

const eggs = "global eggs";

function spam() {
console.log(eggs); // reads global
}

spam(); // prints "global eggs"
console.log(eggs);

Same name local and global

You can have a global and a local variable with the same name, but it is confusing and usually best avoided.

Example:

const eggs = "global";

function spam() {
const eggs = "spam local";
console.log(eggs);
}

function bacon() {
const eggs = "bacon local";
console.log(eggs);
}

spam(); // spam local
bacon(); // bacon local
console.log(eggs); // global

Modifying outer variables

JavaScript doesn't have a global keyword. If a variable is declared with let in the outer scope, the function can access and modify it directly (no special keyword needed).

Example:

let eggs = "global";

function spam() {
eggs = "spam"; // modifies the outer variable
}

spam();
console.log(eggs); // spam

With const, you cannot reassign, so use let for mutable globals.


How to tell if a name is local or global

Rules in JavaScript:

  1. A variable declared at the top level with let/const is global.
  2. A variable declared inside a function with let/const is local.
  3. If you assign to a name without declaring it, JavaScript looks up the scope chain (and may accidentally create a global — always use let/const).

Example:

let eggs = "global";

function spam() {
eggs = "spam"; // modifies global (no let/const)
}

function bacon() {
const eggs = "bacon"; // local (declared with const)
console.log(eggs);
}

function ham() {
console.log(eggs); // reads global
}

spam();
console.log(eggs); // spam

Functions as "black boxes"

You can treat functions as black boxes: know their parameters and return values, but ignore the internal implementation.

Example:

function addTax(amount) {
return amount * 1.1;
}

const total = addTax(100); // you don't care how it computes, just that it returns 110

Exceptions and try/catch

Errors raise exceptions that normally crash the program. try/catch lets you catch and handle them so the program can continue.

Naive version (crashes):

function spam(divideBy) {
if (divideBy === 0) throw new Error("Cannot divide by zero!");
return 42 / divideBy;
}

console.log(spam(2));
console.log(spam(0)); // Error thrown

Handled version:

function spam(divideBy) {
try {
if (divideBy === 0) throw new Error("Cannot divide by zero!");
return 42 / divideBy;
} catch (err) {
console.log("Error: Invalid argument.");
}
}

console.log(spam(2)); // 21
console.log(spam(0)); // prints error, returns undefined

try around calls

You can also put the function calls inside the try block; then any exception raised in them will be caught.

Example:

function spam(divideBy) {
if (divideBy === 0) throw new Error("Cannot divide by zero!");
return 42 / divideBy;
}

try {
console.log(spam(2));
console.log(spam(0));
console.log(spam(1)); // never reached after exception
} catch (err) {
console.log("Error: Invalid argument.");
}

Short program: Zigzag animation

The zigzag program uses an infinite loop, setTimeout/setInterval, and string repetition to animate stars moving back and forth. Press Ctrl+C to stop.

let indent = 0;
let indentIncreasing = true;

const interval = setInterval(() => {
console.log(" ".repeat(indent) + "********");

if (indentIncreasing) {
indent++;
if (indent === 20) indentIncreasing = false;
} else {
indent--;
if (indent === 0) indentIncreasing = true;
}
}, 100);

// Stop with: clearInterval(interval)

Short program: Spike animation

The spike program draws a spike shape that grows and shrinks repeatedly using string replication.

async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

async function spike() {
while (true) {
for (let i = 1; i < 9; i++) {
console.log("-".repeat(i * i));
await sleep(100);
}
for (let i = 7; i > 1; i--) {
console.log("-".repeat(i * i));
await sleep(100);
}
}
}

spike();

Overall idea of the chapter

The chapter shows how to define reusable functions, understand call stacks and variable scopes, avoid overusing globals, and use try/catch to handle errors. JavaScript offers function declarations, arrow functions, block scoping with let/const, and closures that work differently from Python's global keyword, but the core ideas are the same.