Variables
A variable is a region in memory where values can be stored for use in an application. Variables are a fundamental building block in any programming language, and JavaScript is no exception. In this module, you will learn about different types of variables, how to declare and assign values to them, and how to modify and use these values in your program. You will also be introduced to concepts such as scope and data types, which are crucial when working with variables in JavaScript.
Loosely-Typed
JavaScript is a loosely-typed (or dynamically-typed) language, meaning you don’t need to declare the type of a variable when you create it. The JavaScript engine automatically determines the type based on the value you assign.
But internally, JavaScript recognizes these primitive types:
- string - Text values
- number - Numeric values
- boolean - true/false values
- object - Complex data structures
- null - Intentional absence of value
- undefined - Variable declared but not assigned
- symbol - Unique identifiers (advanced)
Understanding Loosely Typed vs. Statically Typed Languages
Loosely Typed Languages
- Definition: In loosely typed languages, also known as dynamically typed languages, you don’t have to explicitly declare the data type of a variable. The language automatically determines the data type based on the assigned value.
- Examples: JavaScript, Python, and Ruby.
- Characteristics:
- Flexibility in variable usage.
- Easier for beginners due to fewer rules for variable declaration.
- Potential for type-related errors at runtime, as type checking happens during execution.
Example in JavaScript:
Statically Typed Languages
- Definition: In statically typed languages, you must declare the data type of a variable at the time of its declaration, and this type cannot be changed later.
- Examples: Java, C#, and C++.
- Characteristics:
- Requires explicit declaration of variable types.
- Can catch type-related errors at compile-time, leading to potentially fewer runtime errors.
- Offers performance optimizations as the compiler knows the exact data types used.
Example in Java:
int number = 5; // number must always be an integer
// number = "Hello"; // Error: incompatible types
Key Differences:
- Type Declaration: Loosely typed languages do not require explicit type declaration, whereas statically typed languages do.
- Error Detection: Statically typed languages can catch type errors at compile-time, while loosely typed languages catch these errors at runtime.
- Flexibility vs. Safety: Loosely typed languages offer more flexibility in using variables, but statically typed languages provide more safety against type-related runtime errors.
Understanding these differences is crucial for programming, as it influences the behavior of variables and error handling in your code.
Declaration: var, let, and const
JavaScript provides three ways to declare variables: var, let, and const.
Modern JavaScript: let and const
In modern JavaScript (ES6+), you should primarily use let and const:
let - For variables that will change:
const - For variables that won’t be reassigned:
const Does NOT Mean Immutable
const prevents reassignment of the variable, but if the value is an object or array, you can still modify its contents:
const person = { name: "Alice" };
person.name = "Bob"; // ✅ Allowed - modifying property
console.log(person); // { name: "Bob" }
// person = { name: "Charlie" }; // ❌ Error - can't reassign
const numbers = [1, 2, 3];
numbers.push(4); // ✅ Allowed - modifying array
console.log(numbers); // [1, 2, 3, 4]
// numbers = [5, 6, 7]; // ❌ Error - can't reassign
Legacy: var
The older var keyword should generally be avoided in modern code:
Why avoid var?
- Function-scoped instead of block-scoped (confusing behavior)
- Can be redeclared accidentally
- Hoisted to the top of the scope
You’ll see var in older code, but use let and const in new projects.
Scope: Block vs. Function
Block scope (applies to let and const):
if (true) {
let blockScoped = "Only available in this block";
console.log(blockScoped); // Works
}
// console.log(blockScoped); // ❌ Error: not defined
if (true) {
const age = 25;
console.log(age); // 25
}
// console.log(age); // ❌ Error: not defined
Function scope (applies to var):
function example() {
if (true) {
var functionScoped = "Available anywhere in the function";
}
console.log(functionScoped); // Works (confusing!)
}
Block and Function Scope in JavaScript
JavaScript distinguishes between two types of scopes: block scope and function scope. Block scope is created within curly braces ({}) and is used with let and const declarations, limiting the visibility of a variable to within that block. Function scope, on the other hand, is defined within functions and is applied to variables declared with var, making them accessible anywhere within the function. Understanding these scopes is crucial for managing variable visibility and lifecycle, and avoiding unintended side effects or errors in your code.
Strict Mode in ES Modules
ES Modules Are Automatically Strict
When using ES Modules (which we use in this course), strict mode is automatically enabled. You don’t need to add "use strict"; manually.
Strict mode prevents common mistakes like:
Type Checking
You can check the type of a variable using typeof:
let i = 1;
console.log(typeof i); // "number"
i = true;
console.log(typeof i); // "boolean"
i = "hello";
console.log(typeof i); // "string"
i = {};
console.log(typeof i); // "object"
i = new Date();
console.log(typeof i); // "object"
console.log(i instanceof Date); // true
For more specific type checking with objects, use instanceof:
const date = new Date();
console.log(date instanceof Date); // true
console.log(date instanceof Object); // true
The Number Type
JavaScript uses 64-bit floating point numbers for all numeric values. This means there’s only one number type (unlike languages with int, float, double, etc.).
Basic Number Usage
let age = 25;
let price = 99.99;
let negative = -42;
let scientific = 3.14e5; // 314000
console.log(age); // 25
console.log(price); // 99.99
console.log(scientific); // 314000
Number Precision and Limits
Numbers use . as the decimal point:
Very large numbers become Infinity:
Invalid calculations result in NaN (Not a Number):
let invalid = 0 / 0;
console.log(invalid); // NaN
console.log(isNaN(invalid)); // true
console.log(isNaN(42)); // false
Number Methods
JavaScript provides several useful methods for formatting numbers:
let value = 2342.23342234;
console.log(value); // 2342.23342234
console.log(value.toFixed(2)); // "2342.23"
console.log(value.toString()); // "2342.23342234"
console.log(value.toLocaleString("da-DK")); // "2.342,233" (Danish format)
console.log(
value.toLocaleString("da-DK", {
maximumFractionDigits: 2,
})
); // "2.342,23"
The Math Object
JavaScript provides a built-in Math object with properties and methods for mathematical constants and functions:
Rounding Numbers:
let number = 4.7;
console.log(Math.round(number)); // 5
console.log(Math.floor(number)); // 4
console.log(Math.ceil(number)); // 5
Generating Random Numbers:
let random = Math.random(); // Between 0 (inclusive) and 1 (exclusive)
console.log(random);
// Random integer between 1 and 10
let randomInt = Math.floor(Math.random() * 10) + 1;
console.log(randomInt);
Finding Maximum and Minimum:
Power and Square Roots:
console.log(Math.pow(2, 3)); // 8 (2 raised to the power of 3)
console.log(Math.sqrt(16)); // 4 (square root of 16)
console.log(Math.abs(-5)); // 5 (absolute value)
Type Conversion: String to Number
Sometimes you need to convert a string to a number. JavaScript provides several methods:
Using parseInt (for integers):
let strInt = "123";
console.log(parseInt(strInt)); // 123
let strFloat = "123.45";
console.log(parseInt(strFloat)); // 123 (only integer part)
let withText = "123px";
console.log(parseInt(withText)); // 123 (stops at first non-digit)
Using parseFloat (for decimal numbers):
let strInt = "123";
console.log(parseFloat(strInt)); // 123
let strFloat = "123.45";
console.log(parseFloat(strFloat)); // 123.45
let withText = "123.45px";
console.log(parseFloat(withText)); // 123.45
Using the Number Constructor:
let strNumber = "456";
console.log(Number(strNumber)); // 456
let invalidStr = "456abc";
console.log(Number(invalidStr)); // NaN (more strict than parseInt)
Unary + Operator (quickest way):
let strNum = "789";
console.log(+strNum); // 789
let strFloatNum = "789.01";
console.log(+strFloatNum); // 789.01
Bitwise OR | Operator (converts to integer):
let strBitwise = "234";
console.log(strBitwise | 0); // 234
let strBitwiseFloat = "234.56";
console.log(strBitwiseFloat | 0); // 234
Using Number.parseInt and Number.parseFloat:
These are essentially the same as global parseInt and parseFloat:
let strParseInt = "345";
console.log(Number.parseInt(strParseInt)); // 345
let strParseFloat = "345.67";
console.log(Number.parseFloat(strParseFloat)); // 345.67
Which Method to Use?
- Use
parseInt()orparseFloat()when parsing user input that might contain text - Use
Number()or+when you want strict conversion (returns NaN if invalid) - Use
parseInt()with a radix parameter for safety:parseInt("10", 10)
Number Operators
| Operator | Description |
|---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division |
% |
Modulus (Remainder after division) |
++ |
Increment (Increases the value by 1) |
-- |
Decrement (Decreases the value by 1) |
** |
Exponentiation (ES2016) |
let a = 10;
let b = 3;
console.log(a + b); // 13
console.log(a - b); // 7
console.log(a * b); // 30
console.log(a / b); // 3.3333...
console.log(a % b); // 1 (remainder)
console.log(a ** b); // 1000 (10^3)
a++; // a is now 11
b--; // b is now 2
Number System Issues
In JavaScript, how you write a number can cause it to be interpreted in different bases like octal (base 8) or hexadecimal (base 16).
Hexadecimal Numbers (base 16) start with 0x:
Octal Numbers (base 8) can be written with 0o prefix:
Binary Numbers (base 2) use 0b prefix:
Avoid Leading Zeros
In older JavaScript (ES5), numbers with leading zeros were interpreted as octal:
Always use explicit prefixes (0x, 0o, 0b) to avoid confusion.
BigInt for Very Large Numbers
BigInt is a newer data type for handling integers larger than 2^53 - 1 (the maximum safe integer for regular numbers). This is useful for cryptography, timestamps, or large database IDs.
// Regular number limit
const maxSafe = Number.MAX_SAFE_INTEGER;
console.log(maxSafe); // 9007199254740991
// Using BigInt (add 'n' suffix)
const bigNumber = 9007199254740991n;
const bigger = bigNumber + 1n; // Must use 'n' for operations
console.log(bigger); // 9007199254740992n
// Convert from regular number
const bigFromNumber = BigInt(123456789);
console.log(bigFromNumber); // 123456789n
BigInt Limitations
- Can’t mix BigInt with regular numbers:
1n + 1will error - Not all browsers support BigInt yet (check compatibility)
- Can’t use with
Mathobject methods
The Boolean Type
Boolean values represent logical truth: true or false. Named after mathematician George Boole, booleans are fundamental to programming logic.
Basic Boolean Usage
let isStudent = true;
let isAdult = false;
if (isStudent) {
console.log("Eligible for student discount");
}
if (!isAdult) {
console.log("Parental consent required");
}
Truthy and Falsy Values
In JavaScript, every value has an inherent “truthiness” when evaluated in a boolean context (like if statements).
Falsy values (evaluate to false):
false0and-0""(empty string)nullundefinedNaN
Everything else is truthy, including:
'0'(string containing zero)[](empty array){}(empty object)- Any non-zero number
- Any non-empty string
// Falsy examples
if (false) {
// Won't execute
}
if (0) {
// Won't execute
}
if ("") {
// Won't execute
}
// Truthy examples
if ("0") {
console.log("String '0' is truthy!"); // Executes
}
if ([]) {
console.log("Empty array is truthy!"); // Executes
}
if ({}) {
console.log("Empty object is truthy!"); // Executes
}
if (42) {
console.log("42 is truthy!"); // Executes
}
Using Double NOT (!!) to Test Truthiness
Use !! to convert any value to its boolean equivalent:
console.log(!!'hello'); // true
console.log(!!42); // true
console.log(!![]); // true
console.log(!!{}); // true
console.log(!!''); // false
console.log(!!0); // false
console.log(!!null); // false
console.log(!!undefined); // false
console.log(!!NaN); // false
Comparison Operators
Always use === or !== for comparisons:
let a = 1;
let b = "1";
// Loose equality (performs type conversion) - AVOID
if (a == b) {
console.log("1 == '1' is true - but confusing!");
}
// Strict equality (checks type first) - RECOMMENDED
if (a === b) {
console.log("This won't execute - different types");
}
// Correct strict comparison
if (a === 1) {
console.log("a is the number 1");
}
Avoid == and !=
The loose equality operators (== and !=) perform type conversion, leading to unexpected results:
console.log(0 == false); // true (confusing!)
console.log('' == false); // true (confusing!)
console.log(null == undefined); // true (confusing!)
// Always use strict equality
console.log(0 === false); // false (correct)
console.log('' === false); // false (correct)
console.log(null === undefined); // false (correct)
Boolean Operators
| Operator | Description |
|---|---|
< |
Less than |
> |
Greater than |
<= |
Less than or equal to |
>= |
Greater than or equal to |
=== |
Strict equality (recommended) |
!== |
Strict inequality (recommended) |
! |
Logical NOT (Inversion) |
&& |
Logical AND |
| | |
Logical OR |
let age = 25;
if (age >= 18 && age < 65) {
console.log("Working age");
}
let hasLicense = true;
let hasInsurance = false;
if (hasLicense && hasInsurance) {
console.log("Can drive");
} else {
console.log("Cannot drive");
}
// Logical OR
if (age < 18 || age > 65) {
console.log("Discount eligible");
}
// Logical NOT
if (!hasInsurance) {
console.log("Need to buy insurance");
}
The String Type
Strings contain Unicode text and are one of the most commonly used data types in JavaScript.
Creating Strings
Use single quotes ('), double quotes ("), or backticks (`) for template literals:
let single = 'Hello';
let double = "World";
let template = `Hello, World!`;
// All three are valid - choose a style and be consistent
String Concatenation
Combine strings with the + operator:
let firstName = "Alice";
let lastName = "Johnson";
let fullName = firstName + " " + lastName;
console.log(fullName); // "Alice Johnson"
Escape Characters
Special characters need to be escaped with backslash (\):
\n- Newline\t- Tab\'- Single quote\"- Double quote\\- Backslash\uNNNN- Unicode character
let text = "Line 1\nLine 2";
console.log(text);
// Line 1
// Line 2
let quote = "He said, \"Hello!\"";
console.log(quote); // He said, "Hello!"
let path = "C:\\Users\\Alice\\Documents";
console.log(path); // C:\Users\Alice\Documents
String Methods
JavaScript strings have many built-in methods:
let txt = "Marx Brothers";
console.log(txt.length); // 13
console.log(txt.toUpperCase()); // "MARX BROTHERS"
console.log(txt.toLowerCase()); // "marx brothers"
console.log(txt.split(" ")); // ["Marx", "Brothers"]
console.log(txt.substring(0, 4)); // "Marx"
console.log(txt.slice(0, -5)); // "Marx Bro"
console.log(txt.charAt(5)); // "B"
// Modern methods (ES6+)
console.log(txt.startsWith("Ma")); // true
console.log(txt.endsWith("rs")); // true
console.log(txt.includes("BRO")); // false (case-sensitive)
console.log(txt.includes("Bro")); // true
console.log(txt.repeat(2)); // "Marx BrothersMarx Brothers"
// Trimming whitespace
let messy = " hello world ";
console.log(messy.trim()); // "hello world"
console.log(messy.trimStart()); // "hello world "
console.log(messy.trimEnd()); // " hello world"
// Replacing text
let original = "Hello World";
console.log(original.replace("World", "JavaScript")); // "Hello JavaScript"
console.log(original.replaceAll("l", "L")); // "HeLLo WorLd" (ES2021)
Template Strings (Template Literals)
Template strings use backticks and allow embedded expressions with ${}:
let name = "Mathias";
let amount = 100;
// Old way (concatenation)
console.log(name + " has DKK " + amount);
// Mathias has DKK 100
// Modern way (template literal)
console.log(`${name} has DKK ${amount}`);
// Mathias has DKK 100
// Complex expressions
console.log(`${name} has DKK ${amount} and next month DKK ${amount * 2}`);
// Mathias has DKK 100 and next month DKK 200
// Multi-line strings
let message = `
Hello ${name},
Your balance is: ${amount}
Thank you!
`;
console.log(message);
Advanced Example - HTML Generation:
function personHTML({ name, hobbies, job }) {
return `<article class="person">
<h3>${name}</h3>
<div>
<div>Hobbies:</div>
<ul>
${hobbies.map((hobby) => `<li>${hobby}</li>`).join("")}
</ul>
</div>
<p>Current job: ${job}</p>
</article>`;
}
console.log(
personHTML({
name: "Mikkel",
hobbies: ["Football", "Fitness"],
job: "Carpenter",
})
);
/*
<article class="person">
<h3>Mikkel</h3>
<div>
<div>Hobbies:</div>
<ul>
<li>Football</li><li>Fitness</li>
</ul>
</div>
<p>Current job: Carpenter</p>
</article>
*/
String Manipulation Libraries
While JavaScript’s built-in string methods are powerful, some developers prefer using libraries for complex string operations.
The most popular string manipulation library is Lodash. It provides utility functions for common programming tasks, including extensive string manipulation.
To install Lodash:
Using Lodash with ES Modules:
import _ from 'lodash';
let str = "hello world";
let capitalized = _.capitalize(str);
console.log(capitalized); // "Hello world"
let camelCase = _.camelCase("hello world example");
console.log(camelCase); // "helloWorldExample"
let snakeCase = _.snakeCase("hello world example");
console.log(snakeCase); // "hello_world_example"
When to Use String Libraries
- For simple string operations, use built-in methods
- Use Lodash when working with complex transformations, data structures, or need cross-browser consistency
- Lodash also provides utility functions for arrays, objects, and functions
The Date Type
JavaScript’s Date object represents dates and times. However, it has some quirks and limitations, which is why many developers use libraries like date-fns or Luxon for more robust date handling.
Moment.js is Legacy
Moment.js was very popular but is now in maintenance mode. The Moment.js team recommends using modern alternatives like date-fns, Luxon, or Day.js for new projects.
Creating Dates
// Current date and time
let now = new Date();
console.log(now.toLocaleString()); // "10/28/2025, 2:30:45 PM" (format depends on locale)
// Specific date from string
let d = new Date("2019-5-14 9:10");
console.log(d.toLocaleString()); // "5/14/2019, 9:10:00 AM"
// Specific date from numbers (year, month, day, hour, minute, second)
d = new Date(2019, 4, 14, 9, 10); // Month is 0-indexed!
console.log(d.toLocaleString()); // "5/14/2019, 9:10:00 AM"
Months Are 0-Indexed!
JavaScript dates use 0-based indexing for months: - 0 = January - 1 = February - … - 11 = December
let christmas = new Date(2025, 11, 24); // December 24, 2025
console.log(christmas.getMonth()); // 11 (not 12!)
let newYear = new Date(2025, 0, 1); // January 1, 2025
console.log(newYear.getMonth()); // 0
This quirk traces back to JavaScript’s early design choices and can be confusing. Always remember: months start at 0, but days start at 1.
Getting Date Components
let d = new Date(2019, 4, 14, 9, 10, 30);
console.log(d.getFullYear()); // 2019
console.log(d.getMonth()); // 4 (May - remember 0-indexed!)
console.log(d.getDate()); // 14 (day of month)
console.log(d.getDay()); // 2 (Tuesday - 0=Sunday, 6=Saturday)
console.log(d.getHours()); // 9
console.log(d.getMinutes()); // 10
console.log(d.getSeconds()); // 30
console.log(d.getTime()); // Milliseconds since Jan 1, 1970
Setting Date Components
let d = new Date(2019, 4, 14);
d.setFullYear(2020);
d.setMonth(11); // December
d.setDate(25); // 25th
d.setHours(15);
d.setMinutes(30);
console.log(d.toLocaleString()); // "12/25/2020, 3:30:00 PM"
Date Arithmetic
Dates can overflow, which is sometimes useful:
let d = new Date(2019, 11, 31); // December 31, 2019
d.setDate(d.getDate() + 1); // Add one day
console.log(d.toLocaleString()); // "1/1/2020..." (automatically rolls over to new year)
// Month overflow example
let d2 = new Date(2019, 12, 24); // Month 12 = January of next year!
console.log(d2.toLocaleString()); // "1/24/2020..." (January 24, 2020)
Formatting Dates
let now = new Date();
// Various formatting options
console.log(now.toString()); // Full string
console.log(now.toDateString()); // "Tue Oct 28 2025"
console.log(now.toTimeString()); // "14:30:45 GMT+0100 (CET)"
console.log(now.toISOString()); // "2025-10-28T13:30:45.123Z"
console.log(now.toLocaleString("da-DK")); // "28.10.2025 14.30.45"
console.log(now.toLocaleString("en-US")); // "10/28/2025, 2:30:45 PM"
// Custom formatting with options
console.log(now.toLocaleString("da-DK", {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}));
// "tirsdag den 28. oktober 2025"
Using date-fns Library (Modern Alternative)
For more robust date handling, use date-fns:
import { format, addDays, differenceInDays } from 'date-fns';
import { da } from 'date-fns/locale';
let now = new Date();
// Format dates easily
console.log(format(now, 'yyyy-MM-dd')); // "2025-10-28"
console.log(format(now, 'dd MMMM yyyy', { locale: da })); // "28 oktober 2025"
// Add days
let nextWeek = addDays(now, 7);
console.log(format(nextWeek, 'yyyy-MM-dd')); // "2025-11-04"
// Calculate differences
let christmas = new Date(2025, 11, 24);
let daysUntil = differenceInDays(christmas, now);
console.log(`${daysUntil} days until Christmas`);
Why Use a Date Library?
- Timezone handling: Native Date has limited timezone support
- Clearer API: More intuitive methods than native Date
- Formatting: Easy, consistent date formatting
- Immutability: Libraries like date-fns don’t mutate dates
- Reliability: Better handling of edge cases and locales