Functions

A function is a reusable block of code designed to perform a particular task. Instead of repeating the same code in multiple places, you write it once inside a function and call it whenever needed.

Functions help keep code organized, readable, and easy to maintain. They can accept inputs (called parameters), do some work, and optionally return a result.

javascript

function greet(name) {
  return "Hello, " + name + "!";
}

console.log(greet("Alice")); // Hello, Alice!
console.log(greet("Bob"));   // Hello, Bob!

Key points about functions:

  • Defined once, called many times.
  • Can accept zero or more parameters.
  • Can return a value using the return keyword.
  • If no return is used, the function returns undefined.

JavaScript supports several ways to define functions. Each has slightly different behavior and syntax:

  • Function Declaration — uses the function keyword with a name.
  • Function Expression — assigns a function to a variable.
  • Arrow Function — a shorter syntax introduced in ES6.
  • Anonymous Function — a function without a name, often used inline.
  • IIFE (Immediately Invoked Function Expression) — runs right after it is defined.

javascript

// Function Declaration
function sayHi() { console.log("Hi!"); }

// Function Expression
const sayHello = function() { console.log("Hello!"); };

// Arrow Function
const sayHey = () => console.log("Hey!");

// IIFE
(function() { console.log("I run immediately!"); })();

A function declaration defines a named function using the function keyword. It is hoisted, meaning you can call it before the line where it is defined in your code.

javascript

// Called before it is defined - works due to hoisting
console.log(add(2, 3)); // 5

function add(a, b) {
  return a + b;
}

Function declarations are fully hoisted to the top of their scope, so they are available throughout the entire scope they belong to.

A function expression assigns a function to a variable. Unlike function declarations, function expressions are not hoisted. You must define them before calling them.

javascript

// Cannot call before definition
// console.log(multiply(2, 3)); // TypeError: multiply is not a function

const multiply = function(a, b) {
  return a * b;
};

console.log(multiply(2, 3)); // 6

Function expressions are useful when you want to pass a function as an argument, return it from another function, or control exactly when a function is created.

Arrow functions are a concise way to write functions, introduced in ES6. They use the => syntax and are always anonymous (no own name).

javascript

// Regular function
const double = function(n) { return n * 2; };

// Arrow function - same thing, shorter syntax
const doubleArrow = (n) => n * 2;

console.log(double(5));      // 10
console.log(doubleArrow(5)); // 10

Key differences of arrow functions:

  • Do not have their own this — they inherit this from the surrounding scope.
  • Cannot be used as constructors (no new keyword).
  • Do not have an arguments object.
  • Cannot use super.
  • Support implicit return when there are no curly braces block.

An anonymous function is a function without a name. It is often used as a callback or assigned to a variable. Both function expressions and arrow functions are typically anonymous.

javascript

// Anonymous function as a callback
setTimeout(function() {
  console.log("Runs after 1 second");
}, 1000);

// Anonymous arrow function as a callback
[1, 2, 3].forEach(n => console.log(n));

Anonymous functions are common in event handlers, array methods like map, filter, and reduce, and anywhere a function is used only once.

The key differences between a function declaration and a function expression are:

  • Hoisting: Function declarations are hoisted; function expressions are not.
  • Name: Declarations always have a name; expressions can be anonymous.
  • Usage: Declarations are statements; expressions are values you can assign or pass around.

javascript

// Declaration - hoisted, works before definition
console.log(square(4)); // 16
function square(n) { return n * n; }

// Expression - NOT hoisted
// console.log(cube(3)); // TypeError
const cube = function(n) { return n * n * n; };
console.log(cube(3)); // 27

Both function expressions and arrow functions can be assigned to variables, but they behave differently in some important ways:

Syntax

javascript

// Function expression
const greet = function(name) {
  return "Hello, " + name;
};

// Arrow function - same result
const greetArrow = (name) => "Hello, " + name;

this

javascript

const obj = {
  name: "JS",
  // function expression has its own 'this'
  regularFn: function() { console.log(this.name); }, // "JS"
  // arrow function inherits 'this' from outer scope
  arrowFn: () => { console.log(this); } // undefined or window
};

obj.regularFn(); // JS
obj.arrowFn();   // undefined (arrow does not bind 'this')

arguments object

javascript

const regularFn = function() {
  console.log(arguments); // works
};

const arrowFn = () => {
  console.log(arguments); // ReferenceError: arguments is not defined
};

regularFn(1, 2, 3);

new keyword

javascript

const Fn = function() { this.value = 42; };
const obj1 = new Fn(); // works fine

const ArrowFn = () => {};
const obj2 = new ArrowFn(); // TypeError: ArrowFn is not a constructor

super

javascript

class Animal {
  speak() { return "..."; }
}

class Dog extends Animal {
  speak() {
    // regular method can use super
    return super.speak() + " Woof!";
  }
}

console.log(new Dog().speak()); // ... Woof!

Arrow functions cannot use super because they do not have their own this binding. Use regular methods in classes when you need super.

Implicit return

javascript

// Arrow function with implicit return (no braces, no return keyword)
const add = (a, b) => a + b;
console.log(add(3, 4)); // 7

// Returning an object - wrap in parentheses
const makeObj = (x) => ({ value: x });
console.log(makeObj(10)); // { value: 10 }

When an arrow function body has no curly braces, the expression is automatically returned. This is called implicit return. To return an object literal, wrap it in parentheses to avoid confusion with a block.

Defining a function does not run it. To execute the code inside a function you must call (or invoke) it by using its name followed by parentheses ().

javascript

function sayHello() {
  console.log("Hello!");
}

sayHello(); // Invokes the function -> Output: Hello!

Functions can also be called from inside other functions:

javascript

function greetUser(name) {
  const message = buildMessage(name);
  console.log(message);
}

function buildMessage(name) {
  return "Welcome, " + name + "!";
}

greetUser("Alice"); // Welcome, Alice!

Parameters are the names listed in the function definition. Arguments are the actual values passed when the function is called.

javascript

// 'a' and 'b' are parameters
function add(a, b) {
  return a + b;
}

// 3 and 5 are arguments
console.log(add(3, 5)); // 8

If you pass fewer arguments than parameters, the missing ones are undefined. If you pass more, the extras are simply ignored (unless you use the arguments object or rest parameters).

Inside a regular function, there is a special array-like object called arguments that holds all the values passed to the function, regardless of the number of defined parameters. It is not available in arrow functions.

javascript

function sum() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}

console.log(sum(1, 2, 3));       // 6
console.log(sum(10, 20, 30, 40)); // 100

arguments is array-like but not a real array. You can convert it to an array using Array.from(arguments) or the spread operator [...arguments]. In modern code, prefer using rest parameters instead.

Default parameters let you set a fallback value for a parameter when no argument (or undefined) is passed. This was introduced in ES6.

javascript

function greet(name = "Guest") {
  return "Hello, " + name + "!";
}

console.log(greet("Alice")); // Hello, Alice!
console.log(greet());        // Hello, Guest!

You can use any expression as a default value, including another function call or another parameter:

javascript

function multiply(a, b = a * 2) {
  return a * b;
}

console.log(multiply(3));    // 3 * 6 = 18
console.log(multiply(3, 4)); // 3 * 4 = 12

In JavaScript, how arguments behave inside a function depends on whether the value is a primitive or an object:

  • Primitives (numbers, strings, booleans) are passed by value. A copy is made, so changes inside the function do not affect the original.
  • Objects and arrays are passed by reference. The function receives a reference to the same object, so changes inside the function affect the original.

Passing by value (primitives)

javascript

function addTen(num) {
  num += 10; // changes local copy only
  console.log("Inside:", num); // Inside: 15
}

let x = 5;
addTen(x);
console.log("Outside:", x); // Outside: 5 (unchanged)

Passing by reference (objects)

javascript

function rename(person) {
  person.name = "Bob"; // modifies the original object
}

const user = { name: "Alice" };
rename(user);
console.log(user.name); // Bob (changed!)

To avoid accidentally mutating objects, create a copy before modifying them inside a function using the spread operator or Object.assign.

  • Function — a reusable block of code that performs a task.
  • Function declaration — hoisted; can be called before its definition.
  • Function expression — not hoisted; assigned to a variable.
  • Arrow function — concise syntax; no own this, arguments, or new.
  • Anonymous function — a function with no name, used inline or as a callback.
  • Parameters — placeholders in the function definition.
  • Arguments — actual values passed when calling a function.
  • arguments object — available in regular functions; holds all passed arguments.
  • Default parameters — provide fallback values for missing arguments.
  • Pass by value — primitives are copied; changes inside the function do not affect the original.
  • Pass by reference — objects are shared; changes inside the function affect the original.