JavaScript is the backbone of modern web technologies, enabling interactive and dynamic user experiences. Below is a guide to the core JavaScript concepts essential for web development.
JavaScript syntax refers to the rules for writing code. Key elements include:
let name = "John"; // Correct
let Name = "Doe"; // Different variable
let age = 25;
console.log(age);
//
for single-line comments and /* */
for multi-line comments.// This is a single-line comment
/* This is a
multi-line comment */
JavaScript is case-sensitive, meaning that variable names with different capitalization are treated as separate variables. For example:
let name = "John"; // 'name' with lowercase n
let Name = "Doe"; // 'Name' with uppercase N is different
Here, name
and Name
are different variables because JavaScript differentiates between uppercase and lowercase letters.
Semicolons are used to terminate statements. While JavaScript has Automatic Semicolon Insertion (ASI) which automatically inserts semicolons where they are needed, it's good practice to explicitly use them. This helps in avoiding errors or confusion in more complex code.
let age = 25; // Use of semicolon for clarity
console.log(age);
Comments are used to add explanations to your code and improve readability. They do not get executed.
//
for a comment that only takes one line.// This is a single-line comment
/* */
for comments spanning multiple lines./* This is a multi-line comment
which can span several lines. */
var
vs let
In JavaScript, you can declare variables using either var
or let
. Both allow you to store data, but there are differences between them:
var
Declarationvar
was used to declare variables in JavaScript before ES6 (ECMAScript 2015). However, it has some issues that make it less useful compared to let
.
var
has **function scope**, meaning it is scoped to the function in which it is declared (or globally if declared outside a function).var
allows variable redeclaration within the same scope without any errors.var name = "Alice";
var name = "Bob"; // No error, redeclaration is allowed
let
Declarationlet
was introduced in ES6 and is preferred for declaring variables in modern JavaScript because it has block-level scope (only accessible within the block in which it is declared).
let
has **block scope**, which means the variable is only accessible within the block (e.g., inside a loop, condition, or function) where it is declared.let
does not allow redeclaration of variables within the same scope, which helps avoid unintended bugs.let age = 25;
let age = 30; // Error: 'age' has already been declared
JavaScript has several data types that can be categorized into two types: Primitive Data Types and Reference Data Types.
let greeting = "Hello, World!";
let name = 'Alice';
let age = 30;
let temperature = 22.5;
true
or false
.let isActive = true;
undefined
.let user;
console.log(user); // Outputs: undefined
let user = null;
let sym = Symbol('description');
Number
type can handle.let bigNum = BigInt(9007199254740991);
let person = {
name: "John",
age: 30,
isActive: true
};
let fruits = ["apple", "banana", "cherry"];
function greet() {
console.log("Hello!");
}
greet(); // Outputs: Hello!
In JavaScript, the scope of a variable refers to the region in which it is accessible. There are three main types of scope: Global Scope, Function ScopeBlock Scope.
A variable declared outside any function or block belongs to the Global Scope. It can be accessed from anywhere in the program, both inside and outside functions.
let globalVar = "I'm global"; // Declared outside any function
function showGlobalVar() {
console.log(globalVar); // Accessible inside function
}
showGlobalVar(); // Outputs: I'm global
console.log(globalVar); // Outputs: I'm global, accessible outside function
In the above example:
globalVar
is accessible both inside the function showGlobalVar()
and outside it.A variable declared inside a function using var
, let
, or const
is only accessible within that function. This is called Function Scope or Local Scope.
function localScopeExample() {
let localVar = "I'm local"; // Declared inside the function
console.log(localVar); // Accessible inside function
}
localScopeExample(); // Outputs: I'm local
console.log(localVar); // Error: localVar is not defined
In the above example:
localVar
is only accessible inside the function localScopeExample()
.localVar
outside the function results in an error because it is scoped to that function.A variable declared inside a block (denoted by {}
, such as inside loops or conditionals) using let
or const
is only accessible within that block. This is called Block Scope.
let
and const
:{
let blockVar = "I'm block scoped"; // Inside a block
console.log(blockVar); // Accessible inside block
}
console.log(blockVar); // Error: blockVar is not defined
In the above example:
blockVar
is only accessible inside the block where it is declared.blockVar
outside the block results in an error because it is block-scoped.var
:{
var blockVar = "I'm block scoped with var"; // Inside a block
console.log(blockVar); // Accessible inside block
}
console.log(blockVar); // Accessible outside block because 'var' is function-scoped
In the above example:
blockVar
is declared using var
, so it is not block-scoped.var
has function scope, the variable is hoisted to the nearest function or global scope and is accessible outside the block.var
, let
, and const
Variable Type | Function Scope | Block Scope | Hoisted |
---|---|---|---|
var |
✅ Yes | ❌ No | ✅ Yes |
let |
✅ Yes | ✅ Yes | ❌ No |
const |
✅ Yes | ✅ Yes | ❌ No |
Explanation:
var
is function-scoped and does not have block scope. It is hoisted to the top of its scope.let
and const
are block-scoped and cannot be accessed outside of the block where they are declared. They are hoisted, but accessing them before the declaration will result in a ReferenceError
.const
is like let
, but it is used to declare variables whose values cannot be reassigned.Hoisting is a JavaScript behavior where variable declarations and function declarations are moved (or "hoisted") to the top of their containing scope before the code is executed. This means you can reference functions and variables before they are declared in the code. However, the way hoisting behaves can differ depending on whether you are using var
, let
, const
, or function declarations.
var
When a variable is declared with var
, it is hoisted to the top of its scope (global or function scope) and initialized with undefined
.
You can reference the variable before its actual declaration, but the value will be undefined
until the assignment occurs.
var
:console.log(a); // ✅ Output: undefined (hoisted but not initialized)
var a = 10;
console.log(a); // Output: 10
Explanation: In this example, the declaration var a;
is hoisted to the top, and a
is initialized with undefined
. The assignment a = 10;
happens in place, and when a
is logged before the assignment, it prints undefined
.
let
and const
Variables declared with let
and const
are also hoisted, but they are not initialized. Trying to access them before declaration leads to a ReferenceError
because they are in the "temporal dead zone" (TDZ) between the start of the block and the line where they are initialized.
let
and const
:console.log(b); // Error: Cannot access 'b' before initialization
let b = 20;
Explanation: In this example, b
is hoisted but not initialized, leading to a ReferenceError
if accessed before its declaration.
Function declarations are fully hoisted, meaning both the function's definition and its body are moved to the top of the scope. This allows you to call a function before its declaration in the code.
greet(); // ✅ Output: "Hello!"
function greet() {
console.log("Hello!");
}
Explanation: The function greet()
is fully hoisted, meaning you can call it before the function is defined in the code, and it will output Hello!
.
greet(); // Error: greet is not a function
var greet = function() {
console.log("Hello!");
};
Explanation: When using a function expression, only the var greet;
declaration is hoisted, not the assignment. Therefore, calling greet()
before the function expression is assigned will result in an error.
Declaration Type | Hoisted to Top? | Initialization | Access Before Declaration |
---|---|---|---|
var |
Yes | Initialized as undefined |
Works, but value will be undefined |
let and const |
Yes | Not initialized (Temporal Dead Zone) | Throws ReferenceError if accessed early |
Function Declarations | Yes | Fully initialized (definition and body) | Works, can be called before declaration |
Function Expressions | No | Assigned to variable at runtime | Cannot be called before assignment |
let
and const
: These keywords help avoid issues with hoisting and "undefined" values, as well as provide block-scoping.
In JavaScript, wrapper types (or wrapper objects) are special objects that provide methods and properties for the corresponding primitive types. Primitives such as String
, Number
, and Boolean
are immutable, meaning their values cannot be changed once assigned. However, the wrapper objects allow us to treat these primitive values as objects, giving us the ability to access methods and properties associated with those types.
.toUpperCase()
for strings, .toFixed()
for numbers, etc.
The String
wrapper is used to access string-related methods like .toUpperCase()
, .slice()
, etc. JavaScript automatically wraps primitive strings in String
objects when methods are called.
let str = "Hello"; // primitive string
console.log(typeof(str)); // "string"
let strObj = new String("Hello"); // String wrapper object
console.log(typeof(strObj)); // "object"
console.log(strObj.toUpperCase()); // "HELLO" (method available on String object)
The Number
wrapper allows us to work with numeric values as objects. It provides access to methods like .toFixed()
and .toPrecision()
for formatting numbers.
let n = 100; // primitive number
console.log(typeof(n)); // "number"
let nObj = new Number(100); // Number object wrapper
console.log(typeof(nObj)); // "object"
console.log(nObj.toFixed(2)); // "100.00" (method available on Number object)
The Boolean
wrapper object allows us to call methods like .toString()
on boolean values.
let bool = true; // primitive boolean
console.log(typeof(bool)); // "boolean"
let boolObj = new Boolean(true); // Boolean object wrapper
console.log(typeof(boolObj)); // "object"
console.log(boolObj.toString()); // "true" (method available on Boolean object)
The BigInt
type is used to handle large integers, exceeding the limit of regular numbers. You can use the BigInt
wrapper to work with large integers, though it's typically unnecessary since BigInt
literals can be used directly.
let bigInt = 12345678901234567890n; // BigInt literal (no need for wrapper)
console.log(typeof(bigInt)); // "bigint"
let bigIntObj = new BigInt(12345678901234567890); // BigInt object wrapper (not necessary)
console.log(typeof(bigIntObj)); // "object"
Symbol
is used to create unique identifiers. While it's generally not required to use a Symbol
object wrapper, JavaScript provides the ability to create one if necessary.
let sym = Symbol("id"); // Symbol primitive
console.log(typeof(sym)); // "symbol"
let symObj = new Symbol("id"); // Symbol object wrapper (not necessary)
console.log(typeof(symObj)); // "object"
Primitive Type | Wrapper Object | Example | Description |
---|---|---|---|
String | String | let str = new String("Hello"); |
Allows string methods like .toUpperCase() , .toLowerCase() |
Number | Number | let num = new Number(100); |
Allows number methods like .toFixed() , .toPrecision() |
Boolean | Boolean | let bool = new Boolean(true); |
Allows boolean methods like .toString() |
BigInt | BigInt | let bigInt = 123n; |
Allows handling large integers |
Symbol | Symbol | let sym = Symbol("id"); |
Creates unique identifiers for objects |
str.toUpperCase()
, JavaScript temporarily creates a String
wrapper for the primitive string.new String("Hello")
), it's not typically necessary. Using the primitive directly is simpler and avoids unnecessary complexity.Arithmetic operators are used to perform basic mathematical operations like addition, subtraction, multiplication, division, etc.
Operator | Description | Example | Output |
---|---|---|---|
+ | Addition | let sum = 5 + 3; |
8 |
- | Subtraction | let diff = 10 - 4; |
6 |
* | Multiplication | let product = 4 * 2; |
8 |
/ | Division | let division = 10 / 2; |
5 |
% | Modulo (Remainder) | let remainder = 7 % 2; |
1 |
++ | Increment (adds 1) | let count = 3; count++; |
4 |
-- | Decrement (subtracts 1) | let count = 3; count--; |
2 |
Comparison operators are used to compare two values and return a boolean value (`true` or `false`).
Operator | Description | Example | Output |
---|---|---|---|
== | Loose equality (compares values only) | 5 == "5"; |
true |
=== | Strict equality (compares value and type) | 5 === "5"; |
false |
!= | Loose inequality (compares values only) | 5 != 3; |
true |
!== | Strict inequality (compares value and type) | 5 !== "5"; |
true |
> | Greater than | 5 > 3; |
true |
< | Less than | 5 < 3; |
false |
>= | Greater than or equal to | 5 >= 5; |
true |
<= | Less than or equal to | 5 <= 3; |
false |
Logical operators are used to combine multiple conditions and return a boolean result.
Operator | Description | Example | Output |
---|---|---|---|
&& | Logical AND (both conditions must be true) | true && false; |
false |
|| | Logical OR (at least one condition must be true) | true || false; |
true |
! | Logical NOT (inverts the boolean value) | !true; |
false |
Assignment operators are used to assign values to variables and perform operations in a single step.
Operator | Description | Example | Output |
---|---|---|---|
= | Assigns a value to a variable | let x = 10; |
x = 10 |
+= | Adds and assigns | x += 5; |
x = 15 |
-= | Subtracts and assigns | x -= 5; |
x = 5 |
*= | Multiplies and assigns | x *= 2; |
x = 30 |
/= | Divides and assigns | x /= 2; |
x = 15 |
%= | Assigns the remainder of division | x %= 2; |
x = 1 |
The ternary operator is a shorthand for an if-else
statement.
let age = 18;
let canVote = age >= 18 ? "Yes" : "No"; // If age >= 18, return "Yes", otherwise "No"
The typeof
operator is used to determine the type of a variable or value.
typeof "hello"; // "string"
typeof 42; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (this is a quirk in JavaScript)
Conditional statements are used to execute code based on whether a condition is true or false.
The if-else
statement allows you to run a block of code if a specified condition is true, and optionally run another block if the condition is false.
let age = 20;
if (age >= 18) {
console.log("Adult");
} else {
console.log("Minor");
}
The switch
statement evaluates an expression and matches it with corresponding case
labels.
let day = "Monday";
switch (day) {
case "Monday":
console.log("Start of the week");
break;
case "Friday":
console.log("Almost weekend");
break;
default:
console.log("Regular day");
}
Loops are used to repeat a block of code multiple times until a certain condition is met.
The for
loop is used when you know the number of iterations you need.
for (let i = 0; i < 5; i++) {
console.log(i);
}
The while
loop executes as long as a specified condition evaluates to true.
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
The do-while
loop runs the block of code at least once before checking the condition.
let i = 0;
do {
console.log(i);
i++;
} while (i < 5);
The break
and continue
statements control the flow inside loops.
The break
statement is used to exit a loop prematurely.
for (let i = 0; i < 5; i++) {
if (i === 3) break; // Exits the loop when i is 3
console.log(i);
}
The continue
statement is used to skip the current iteration of a loop and continue with the next iteration.
for (let i = 0; i < 5; i++) {
if (i === 3) continue; // Skips the iteration when i is 3
console.log(i);
}
In JavaScript, an array is a special type of object used to store multiple values in a single variable. Arrays can hold various data types, including numbers, strings, objects, and even other arrays. Arrays also have the ability to dynamically resize as needed.
There are two common ways to create arrays in JavaScript:
The Array Literal
syntax is the most common and recommended way to create an array. It uses square brackets []
to define the array elements.
let fruits = ["Apple", "Banana", "Orange"];
new Array()
(Not Recommended)The new Array()
constructor is less commonly used. It allows you to create an array, but it can lead to some unexpected behavior if not used correctly. You can use it by passing elements as arguments, or by specifying the length of the array.
let colors = new Array("Red", "Green", "Blue");
You can also create an empty array and then assign values dynamically using indexing:
let a = [];
a[0] = 1;
a[1] = 2;
a[2] = 3;
Array elements in JavaScript are accessed using zero-based indexing, meaning the first element is at index 0.
let fruits = ["Apple", "Banana", "Orange"];
console.log(fruits[0]); // Output: Apple
console.log(fruits[1]); // Output: Banana
console.log(fruits[2]); // Output: Orange
Arrays have built-in properties that help you work with them more easily. One of the most commonly used properties is length
, which returns the number of elements in the array.
let fruits = ["Apple", "Banana", "Orange"];
console.log(fruits.length); // Output: 3
JavaScript provides many built-in methods to manipulate arrays. Below are some of the most common ones:
Method | Description | Example |
---|---|---|
push() | Adds an element to the end of an array. | arr.push("Mango") |
pop() | Removes the last element of an array. | arr.pop() |
shift() | Removes the first element of an array. | arr.shift() |
unshift() | Adds an element to the beginning of an array. | arr.unshift("Grapes") |
indexOf() | Finds the index of an element in the array. | arr.indexOf("Banana") |
includes() | Checks if an element exists in the array. | arr.includes("Apple") |
splice() | Adds or removes elements from an array at a specific index. | arr.splice(1, 1, "Mango") |
slice() | Extracts a portion of an array. | arr.slice(1, 3) |
join() | Converts an array into a string. | arr.join(", ") |
reverse() | Reverses the order of the elements in the array. | arr.reverse() |
sort() | Sorts the elements of an array. | arr.sort() |
map() | Creates a new array by transforming each element. | arr.map(x => x * 2) |
filter() | Creates a new array with elements that meet a condition. | arr.filter(x => x > 10) |
forEach() | Iterates over each element of the array. | arr.forEach(x => console.log(x)) |
let numbers = [10, 20, 30, 40];
// Add elements
numbers.push(50); // [10, 20, 30, 40, 50]
numbers.unshift(5); // [5, 10, 20, 30, 40, 50]
// Remove elements
numbers.pop(); // [5, 10, 20, 30, 40]
numbers.shift(); // [10, 20, 30, 40]
// Slice and Splice
let newNumbers = numbers.slice(1, 3); // [20, 30]
numbers.splice(2, 1, 25); // [10, 20, 25, 40]
// Iterating
numbers.forEach(num => console.log(num)); // Prints each number
// Mapping
let doubled = numbers.map(num => num * 2); // [20, 40, 50, 80]
console.log(numbers);
console.log(doubled);
There are several ways to loop through an array in JavaScript.
for
LoopThe traditional for
loop can be used to iterate through an array using its length
property.
let fruits = ["Apple", "Banana", "Orange"];
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]); // Prints each fruit
}
forEach()
The forEach()
method executes a function on each element of the array.
let fruits = ["Apple", "Banana", "Orange"];
fruits.forEach(fruit => {
console.log(fruit); // Prints each fruit
});
You can create arrays inside arrays, known as multidimensional arrays or nested arrays.
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(matrix[1][2]); // Output: 6 (Accessing second row, third column)
You can use Array.isArray()
to check whether a variable is an array or not.
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // Output: true
console.log(Array.isArray("Hello")); // Output: false
A JavaScript function is a set of statements that are designed to perform a specific task or calculate a value. Functions help break down complex tasks into smaller, reusable, and more manageable pieces of code. Functions take input parameters, perform their designated task, and return an output.
To create a function in JavaScript, we follow this basic structure:
function functionName(parameters) {
// function body
// statements
}
Example:
function greet() {
console.log("Hello, World!");
}
In this example, greet
is a named function that logs a greeting message to the console.
Named functions are the most common type of functions in JavaScript. They are defined with a name that can be used to invoke them.
function add(a, b) {
return a + b;
}
console.log(add(5, 3)); // Output: 8
In this case, the function add
takes two parameters (a
and b
), adds them, and returns the result.
Arrow functions are a more concise way to write functions. They can be used for single expressions and are often used when the function body is small.
Arrow functions can either have an expression body or a block body.
Arrow Function with an Expression Body:
let multiply = (a, b) => a * b;
console.log(multiply(4, 5)); // Output: 20
Arrow Function with a Block Body:
let greet = (name) => {
console.log("Hello, " + name + "!");
};
greet("Alice"); // Output: Hello, Alice!
Function expressions assign a function to a variable. These functions can be anonymous or named, and the function's value is stored in the variable. You can call the function using the variable name.
let greet = function(name) {
return 'Hello ' + name;
};
console.log(greet("Bob")); // Output: Hello Bob
Anonymous functions are those that are defined without a name. These are commonly used when functions are passed as arguments to other functions.
let sayHello = function() {
console.log("Hello!");
};
sayHello(); // Output: Hello!
Anonymous functions can also be used with event handlers or other operations like callbacks.
The code inside a function only executes when the function is called or invoked. This is how we invoke (call) a function in JavaScript:
greet(); // Call the function
When the function greet
is invoked, the code inside the function body is executed, and any results (like a return value) can be used.
Example of Function Invocation:
function add(a, b) {
return a + b;
}
let result = add(10, 5);
console.log(result); // Output: 15
JavaScript has a behavior called hoisting, where function declarations are moved to the top of their containing scope before execution. This means you can call a function before it is defined in the code.
Example:
greet(); // This works due to hoisting
function greet() {
console.log("Hello!");
}
Here, even though the greet
function is called before it's declared, it works because JavaScript hoists the function declaration to the top of the scope.
In JavaScript, every time a function is created, it has its own scope. This means that variables declared inside a function are not accessible outside that function. The scope of a function is often referred to as local scope, while variables outside a function exist in the global scope.
Example:
function greet() {
let message = "Hello!";
console.log(message); // Accessible inside the function
}
greet(); // Output: Hello!
console.log(message); // Error: message is not defined (because it's outside the function)
In the above example, the variable message
is accessible only inside the function greet
. Outside of the function, it cannot be accessed because it's a local variable.
Functions can return a value using the return
keyword. This allows functions to produce an output that can be used later in the program.
Example:
function add(a, b) {
return a + b;
}
let result = add(5, 7);
console.log(result); // Output: 12
In this example, the function add
takes two parameters (a
and b
), adds them, and returns the result. The result is then stored in the variable result
.
Functions can accept parameters, which are values passed into the function when it is called. These parameters allow functions to perform operations on different input values. When you call the function, you provide arguments for each parameter.
Example:
function multiply(a, b) {
return a * b;
}
let product = multiply(3, 4);
console.log(product); // Output: 12
Here, a
and b
are the parameters, and 3
and 4
are the arguments passed when calling the function.
JavaScript functions are powerful tools for organizing and reusing code. They allow you to break down tasks into manageable, self-contained blocks. Functions can be defined in several ways, each offering unique benefits. Understanding how to use functions effectively is crucial for writing clean, maintainable JavaScript code.
The three main concerns in web application development are:
Keeping the three concerns as separate as possible improves the delivery of the application to a variety of user agents, such as graphic browsers, text-only browsers, assistive technologies for disabled users, and mobile devices. It also aligns with the principle of progressive enhancement—starting with the basic HTML-only experience for simple user agents and progressively adding features for more capable browsers.
If the browser supports CSS, the user gets an enhanced presentation. If it supports JavaScript, additional interactive features improve the user experience.
In practice, applying the separation of concerns in web development involves the following steps:
href="#"
), and forms work and submit correctly.onclick
) or inline styles, as these violate the separation of concerns and mix behavior/presentation with content.These practices ensure that the page remains accessible, functional, and adaptable across different environments, enhancing user experience and supporting progressive enhancement.