Functions

Course 1 · Ch 5
Functions: Declarations, Expressions, Arrow Functions, Scope
Three different ways to write a function in JavaScript — and why the differences genuinely matter, not just stylistically

PHP Fundamentals Chapter 5 covered one way to define a function. JavaScript has three distinct syntaxes, each with real behavioural differences — not just three ways to write the same thing. This chapter covers all three, plus scope, which works similarly to PHP but with one important new wrinkle.

Function Declarations

function greet(name) { console.log(`Hello, ${name}!`); } greet("Philip"); // "Hello, Philip!"

The most familiar style — looks almost identical to PHP's function syntax (Fundamentals Chapter 5), minus the $ prefix on parameters.

Function Expressions

const greet = function(name) { console.log(`Hello, ${name}!`); }; greet("Philip");

Here the function itself is treated as a value, assigned to a const variable — exactly like assigning a number or string. This reflects something genuinely fundamental about JavaScript: functions are values, storable in variables, passable as arguments, returnable from other functions ("first-class functions"). Chapter 6's array methods (map, filter) rely entirely on this.

Arrow Functions

const greet = (name) => { console.log(`Hello, ${name}!`); }; // Single parameter: parentheses are optional const square = n => n * n; // single-expression body: implicit return, no braces or "return" needed console.log(square(5)); // 25

Arrow functions are a more compact syntax, introduced in 2015 alongside let/const. A single-expression arrow function (like square above) automatically returns that expression's value — no { } braces or explicit return keyword needed, genuinely useful for short, simple functions.

Multi-line arrow functions need braces AND an explicit return
const square = n => { return n * n; }; — once braces are added for multiple statements, the implicit return disappears, and return must be written explicitly, exactly like a normal function. Forgetting this is a genuinely common beginner mistake.

Default Parameters

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

Identical idea to PHP's default parameters from Fundamentals Chapter 5 — a fallback value used when the argument is omitted entirely.

Function vs Arrow Function — A Genuinely Important Difference: this

const counter = { count: 0, increment: function() { this.count++; // "this" refers to the counter object — works correctly console.log(this.count); }, incrementBroken: () => { this.count++; // "this" does NOT refer to counter here — arrow functions don't have their own "this" } }; counter.increment(); // 1 — works correctly

Regular functions get their own this, determined by how they're called. Arrow functions deliberately do not have their own this — they inherit it from their surrounding context instead. This matters significantly once objects (Chapter 7) and classes (Intermediate Chapter 6) are introduced; for now, the practical rule is: prefer regular function syntax for object methods, arrow functions for short standalone helpers and callbacks.

StyleHas its own this?Implicit return?Good for
function name() {}YesNoObject methods, general-purpose functions
const f = function() {}YesNoAssigning a function to a variable, conditionally
const f = () => {}No (inherits)Only if single-expression, no bracesShort callbacks, array methods (Ch 6)

Scope — Mostly Like PHP, With One New Wrinkle

function outer() { let x = 10; // local to this function, like PHP Fundamentals Chapter 5 function inner() { console.log(x); // 10 — inner functions CAN see variables from their enclosing function } inner(); } outer();

A nested function can read variables from the function it's defined inside — this is new compared to PHP, where functions are never nested this way. The outer function's variables remain inaccessible from completely outside, exactly like PHP — but a function defined inside another one has visibility into its parent's scope. This "remembering" behaviour is called a closure, covered in full depth in Intermediate Chapter 2; this chapter just introduces that nested functions can see outer variables.

outer() scope let x = 10 inner() scope console.log(x) → 10
inner() can read outer()'s x, since it's defined inside outer's scope — visibility flows inward, not outward

Coding Challenges

Challenge 1

Write the same function — isEven(number), returning true/false — three different ways: as a function declaration, as a function expression assigned to a const, and as an arrow function. Test all three with the same input and confirm they all produce identical results.

📄 View solution
Challenge 2

Write an arrow function calculateArea that takes width and height (with height defaulting to width, so it also works for squares with one argument) and returns their product using an implicit return. Call it three different ways and console.log() each result.

📄 View solution
Challenge 3

Write a function makeMultiplier(factor) that returns a NEW function — one that takes a number and multiplies it by factor. Use it to create double (factor 2) and triple (factor 3), then test both. Explain in a comment why the inner function can still access factor after makeMultiplier has already finished running.

📄 View solution

Chapter 5 Quick Reference

  • function name() {} — declaration; familiar, has its own this
  • const f = function() {} — expression; function as a value assigned to a variable
  • const f = () => {} — arrow function; no own this, implicit return for single expressions
  • Default parameters: function f(x = "default")
  • this differs between regular functions (own this) and arrow functions (inherited) — matters for object methods
  • Nested functions see outer variables — visibility flows inward; this is the basis of closures (Intermediate Ch 2)
  • Functions are values — "first-class functions," the foundation for array methods in Chapter 6
  • Next chapter: arrays — methods like map, filter, forEach, reduce