This episode covers how to wrap logic into reusable functions: function declarations, parameters, and return values. You also understand global, function, and block variable scope, plus the hoisting behavior that often confuses beginners.

Code written in a straight line from top to bottom quickly becomes a mess as a program grows. Functions are JavaScript's answer to this problem: a named block of logic that can be called whenever needed, so you don't rewrite the same code over and over.
Episode 4 covers two related things. First, functions themselves: how to declare them, how to send data through parameters, and how to receive results via return. Second, scope: the rules for where a variable can be accessed, which determines whether a variable is safe to use inside or outside a function.
Scope and functions are an inseparable pair. Once you understand both, your code will be better structured and easier to debug.
The most basic way to define a function is with the function keyword:
function sapa(nama) {
return `Halo, ${nama}!`;
}
console.log(sapa("Arman"));function sapa(nama) declares a function named sapa that accepts one parameter nama and returns a string. Calling it with sapa("Arman") produces Halo, Arman!. The return keyword determines the returned value; without return, a function returns undefined.
A function should ideally do one clear job. Functions that mix many responsibilities are hard to test and hard to reuse. A simple correct example:
function hitungLuasPersegi(sisi) {
return sisi * sisi;
}
function hitungLuasSegitiga(alas, tinggi) {
return (alas * tinggi) / 2;
}
console.log(hitungLuasPersegi(5));
console.log(hitungLuasSegitiga(6, 4));hitungLuasPersegi(5) returns 25, and hitungLuasSegitiga(6, 4) returns 12. Each function has one clear responsibility, and both can be called at any time. This is the pattern you'll maintain throughout the series.
A function can accept many parameters, and the caller is responsible for sending their values in the same order:
function hitungDiskon(harga, persen) {
return harga - (harga * persen) / 100;
}
console.log(hitungDiskon(200000, 25));hitungDiskon(200000, 25) returns 150000 — a price of 200000 reduced by a 25 percent discount. Parameters behave like local variables inside the function: their values come from the caller, and changes to them don't leak out.
Every return statement immediately stops function execution. Code after return never runs:
function klasifikasiNilai(nilai) {
if (nilai >= 75) {
return "Lulus";
}
return "Belum lulus";
}
console.log(klasifikasiNilai(80));
console.log(klasifikasiNilai(60));return "Lulus" returns the result and ends the function; the next line only runs if the first condition is false. The early return pattern like this makes functions easier to read than a chain of else.
Scope determines where a variable can be accessed. JavaScript has three main levels:
let and const variables inside {}, only valid within that block.const global = "bisa diakses di mana saja";
if (true) {
const lokal = "hanya di dalam blok";
console.log(global);
console.log(lokal);
}
console.log(global);const lokal is declared inside the if block and can't be accessed outside that block — trying to do so triggers a ReferenceError. This is what distinguishes let and const from var, which doesn't respect block scope.
Variables declared inside a function are also hidden from the outside:
function proses() {
const rahasia = "tidak terlihat dari luar";
return rahasia;
}
console.log(proses());const rahasia only lives while the function executes and can't be accessed from an outer scope. Trying to call rahasia outside the function will error. By hiding internal variables, functions don't interfere with each other.
Besides declarations, functions can be stored in variables — this is called a function expression:
const kaliDua = function (angka) {
return angka * 2;
};
console.log(kaliDua(7));const kaliDua = function (angka) stores an anonymous function in a variable. This function can only be called after the declaration, because variables aren't hoisted. This small difference is still important to understand.
Hoisting is JavaScript's behavior of "lifting" function declarations to the top of their scope, so a function declaration can be called before its line is written:
console.log(jumlahkan(3, 4));
function jumlahkan(a, b) {
return a + b;
}The code above runs without error because function jumlahkan is fully hoisted. In contrast, a function expression stored in const is not hoisted and will trigger a ReferenceError if called too early.
Warning
Distinguish the three errors that appear often: ReferenceError means a variable was never declared in that scope, TypeError means the variable exists but isn't the function or object you thought, and SyntaxError means the code is written with incorrect syntax. Reading error names is your first debugging skill.
Episode 4 let you organize code with functions: declaring them, sending data through parameters, receiving results via return, understanding global, function, and block scope, and distinguishing hoisted function declarations from non-hoisted function expressions.
Key takeaways:
return determines a function's result and stops execution.let and const variables respect block scope; var does not.In the next episode 5 we'll cover arrays, objects, and basic data structures — storing collections of values with arrays, modeling entities with objects, building nested data, and getting to know Set and Map as additional data structures. This is the foundation every following episode will use.