function
, arrow functions)Functions are one of the most fundamental building blocks in JavaScript. They allow you to group reusable code into named blocks that can be called whenever needed. JavaScript supports multiple ways to declare functions, with two of the most common being:
In this section, you'll learn how to use both styles, understand their differences, and see when each is most appropriate.
The traditional way to declare a function uses the function
keyword.
Syntax:
function functionName(parameters) {
// code to execute
return result;
}
Example – Add two numbers:
function add(a, b) {
return a + b;
}
console.log(add(3, 4)); // 7
Example – Greet a user:
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('Alice')); // Hello, Alice!
Arrow functions offer a shorter syntax and are commonly used in modern JavaScript, especially for small or anonymous functions.
Syntax:
const functionName = (parameters) => {
// code to execute
return result;
};
If the function has only one expression, you can omit the braces and return
keyword:
const add = (a, b) => a + b;
Example – Greet a user (arrow version):
const greet = name => `Hello, ${name}!`;
console.log(greet('Bob')); // Hello, Bob!
Feature | Function Declaration | Arrow Function |
---|---|---|
Syntax | Verbose | Concise |
this binding |
Dynamic (this depends on caller) |
Lexical (this is inherited from scope) |
Hoisting | Yes (can be used before defined) | No (must be declared first) |
Best for | Methods, complex logic | Callbacks, one-liners |
this
BehaviorOne of the biggest differences between the two styles is how they handle the this
keyword.
Function Declaration:
const user = {
name: 'Alice',
greet: function () {
console.log(`Hi, I'm ${this.name}`);
}
};
user.greet(); // Hi, I'm Alice
Arrow Function (wrong in this case):
const user = {
name: 'Bob',
greet: () => {
console.log(`Hi, I'm ${this.name}`);
}
};
user.greet(); // Hi, I'm undefined
Arrow functions do not have their own
this
. They inherit it from the surrounding context, which can lead to unexpected results when used in object methods.
this
— prefer function declarations for methods that rely on object context.By mastering both styles, you'll be able to write clean, modern JavaScript code that's both expressive and efficient.
Functions in JavaScript behave much like machines: you give them input (parameters), they perform a task, and they may produce output (a return value). Understanding how to pass data into functions and get results back is essential to writing flexible, reusable code.
Parameters are like variables listed in a function’s definition. They represent values that will be passed into the function when it’s called.
Arguments are the actual values you pass when calling the function.
Sometimes, a function doesn't need any input — it simply performs an action.
Example – Say Hello:
function sayHello() {
console.log('Hello!');
}
sayHello(); // Output: Hello!
Example – Greet a user by name:
function greet(name) {
console.log(`Hello, ${name}!`);
}
greet('Alice'); // Output: Hello, Alice!
Here, name
is a parameter, and 'Alice'
is the argument passed to it.
You can define as many parameters as you need, separated by commas.
Example – Add two numbers:
function add(a, b) {
return a + b;
}
let result = add(5, 3);
console.log(result); // 8
If a caller doesn’t provide an argument, you can define a default value to use instead.
Example – Greet with a default name:
function greet(name = 'Guest') {
console.log(`Welcome, ${name}!`);
}
greet(); // Welcome, Guest!
greet('Charlie'); // Welcome, Charlie!
This makes your functions more robust and error-tolerant.
A function can return a value using the return
keyword. This lets you store or use the result of a computation later in your code.
Example – Calculate area of a rectangle:
function calculateArea(width, height) {
return width * height;
}
let area = calculateArea(5, 4);
console.log(area); // 20
Without return
, the function would perform the calculation but not give the result back to the code that called it.
Think of a function like a coffee machine:
If you press the button without choosing sugar, maybe it adds none (default value). When the brewing is done, you get coffee (the return value), which you can drink (use in your program).
Concept | Example | Purpose |
---|---|---|
No parameters | function sayHi() {} |
Executes fixed code |
One parameter | function greet(name) {} |
Accepts a single value |
Multiple parameters | function add(a, b) {} |
Accepts two or more values |
Default values | function greet(name = 'Guest') {} |
Fallback when no argument passed |
Return a value | return a + b; |
Outputs a result from the function |
Knowing how to pass data into functions and return results lets you build custom, reusable tools for your code—just like mini-programs inside your program.
In JavaScript, functions are first-class citizens, meaning they can be treated like any other value — stored in variables, passed as arguments, or returned from other functions. One way to create functions that leverages this flexibility is through function expressions.
A function expression defines a function inside an expression, typically assigning it to a variable.
Syntax:
const sayHello = function() {
console.log('Hello!');
};
Unlike function declarations (function sayHello() {}
), function expressions are not hoisted, which means you cannot call them before they are defined.
You can store a function in a variable just like a number or string.
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(4, 5)); // 20
Because functions can be stored in variables, they can also be passed as arguments to other functions. This is useful for things like event handling or array processing.
Example – Using a callback with setTimeout
:
setTimeout(function() {
console.log('This message appears after 2 seconds');
}, 2000);
Here, the anonymous function (a function without a name) is passed directly as an argument to setTimeout
.
Functions without a name are called anonymous functions. They are often used in function expressions or as callbacks:
const greet = function(name) {
console.log(`Hello, ${name}!`);
};
greet('Alice'); // Hello, Alice!
You can also write anonymous arrow functions:
setTimeout(() => console.log('Done!'), 1000);
Feature | Function Declaration | Function Expression |
---|---|---|
Syntax | function foo() {} |
const foo = function() {} |
Hoisting | Hoisted (can call before defined) | Not hoisted (must define first) |
Naming | Usually named | Can be anonymous or named |
Usage | General purpose | When assigning or passing functions |
Understanding function expressions unlocks much of JavaScript’s flexibility and lets you write more dynamic and functional code.
Understanding variable scope and hoisting is essential for writing reliable JavaScript code. These concepts determine where variables are accessible and how they behave during code execution.
Scope defines the region of your code where a variable is available. In JavaScript, there are three main types of scope:
Variables declared outside any function or block have global scope and can be accessed anywhere in your code.
let globalVar = 'I am global';
function showVar() {
console.log(globalVar); // Accessible here
}
showVar(); // Output: I am global
console.log(globalVar); // Output: I am global
Variables declared inside a function are only accessible within that function.
function myFunc() {
let funcVar = 'I am local to myFunc';
console.log(funcVar); // Works here
}
myFunc();
console.log(funcVar); // Error: funcVar is not defined
Variables declared with var
and let
inside functions are both function-scoped, meaning they exist throughout the function.
Block scope means variables are only accessible inside the nearest pair of {}
, such as in if
statements, loops, or blocks.
let
and const
are block-scoped.var
is not block-scoped — it’s function-scoped.Example:
if (true) {
let blockVar = 'I exist only in this block';
var varVar = 'I am function-scoped';
}
console.log(blockVar); // Error: blockVar is not defined
console.log(varVar); // Output: I am function-scoped
var
vs. let
vs. const
Keyword | Scope | Can be Reassigned? | Can be Redeclared in Same Scope? |
---|---|---|---|
var |
Function scope | Yes | Yes |
let |
Block scope | Yes | No |
const |
Block scope | No (constant) | No |
Example of let
and const
:
let count = 1;
count = 2; // Allowed
const max = 10;
max = 20; // Error: Assignment to constant variable
Hoisting is JavaScript’s behavior of moving variable and function declarations to the top of their scope during compilation, before code runs.
This means you can sometimes use variables and functions before they are declared — but the details vary depending on how they were declared.
var
var
declarations are hoisted and initialized with undefined
.
console.log(myVar); // Output: undefined
var myVar = 10;
Behind the scenes, JavaScript treats it like:
var myVar;
console.log(myVar); // undefined
myVar = 10;
let
and const
let
and const
declarations are hoisted but not initialized. Accessing them before declaration causes a ReferenceError due to the Temporal Dead Zone (TDZ).
console.log(myLet); // ReferenceError
let myLet = 5;
sayHi(); // Works!
function sayHi() {
console.log('Hi!');
}
var
, let
, or const
depending on how they are assigned.sayHello(); // Error or undefined behavior depending on declaration
const sayHello = function() {
console.log('Hello!');
};
Concept | Description | Example |
---|---|---|
Global scope | Variables available anywhere | let x = 5; |
Function scope | Variables available only inside a function | function() { var y = 10; } |
Block scope | Variables available only inside {} blocks (let , const ) |
if (true) { let z = 3; } |
Hoisting | Declarations moved to top; var initialized to undefined , let /const not initialized |
console.log(a); var a = 1; |
Temporal Dead Zone (TDZ) | Time before let or const declaration when accessing them throws an error |
console.log(b); let b = 2; |
Understanding scope and hoisting helps you avoid bugs, write cleaner code, and debug issues related to variable accessibility and initialization.
A closure is a powerful and important concept in JavaScript. Simply put, a closure is when a function remembers the environment in which it was created, even after that environment has finished executing.
This might sound complicated, but closures let you write functions that keep access to variables defined outside themselves — which can be incredibly useful.
When a function is defined inside another function, the inner function retains access to the variables of the outer function, even after the outer function has returned. This saved environment is called a closure.
Let's build a function that creates counters — each counter remembers its own count:
function createCounter() {
let count = 0; // This variable is "closed over"
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
What’s happening here?
createCounter()
defines a local variable count
and returns an anonymous function.count
, remembering it.createCounter()
finished executing, the inner function still has access to count
.counter()
, it updates and returns the updated count
.Closures also help when you want to generate customized functions:
function greetGenerator(greeting) {
return function(name) {
return `${greeting}, ${name}!`;
};
}
const sayHello = greetGenerator('Hello');
const sayHi = greetGenerator('Hi');
console.log(sayHello('Alice')); // Hello, Alice!
console.log(sayHi('Bob')); // Hi, Bob!
Here, each generated function remembers the greeting
passed to the outer function.
Closures are everywhere in JavaScript — in event handlers, callbacks, and many libraries — because they let you write more flexible, modular, and powerful code.
By understanding closures, you'll unlock deeper JavaScript concepts and write code that’s both elegant and effective.