Part 2: Variables and Data Types

The Building Blocks of Every Program


Variables: Storing Values

Variables are named containers for storing data values. JavaScript provides three keywords for declaring variables: var, let, and const.

The let Keyword

let declares a block-scoped variable that can be reassigned.
javascript
let age = 25; console.log(age); // 25 age = 26; // Reassignment is allowed console.log(age); // 26 // Block scope if (true) { let message = 'Hello'; console.log(message); // 'Hello' } // console.log(message); // ReferenceError: message is not defined

The const Keyword

const declares a block-scoped variable that cannot be reassigned.
javascript
const PI = 3.14159; console.log(PI); // 3.14159 // PI = 3.14; // TypeError: Assignment to constant variable // const requires initialization // const x; // SyntaxError: Missing initializer in const declaration const x = 10; // Correct
Important: const prevents reassignment, not mutation.
javascript
const person = { name: 'John' }; // This works - we're mutating the object, not reassigning person.name = 'Jane'; console.log(person.name); // 'Jane' // This fails - we're trying to reassign // person = { name: 'Jane' }; // TypeError const numbers = [1, 2, 3]; numbers.push(4); // Works - mutation console.log(numbers); // [1, 2, 3, 4] // numbers = [1, 2, 3, 4]; // TypeError - reassignment

The var Keyword (Legacy)

var is function-scoped and has hoisting behavior that can lead to bugs.
javascript
// var is function-scoped, not block-scoped function example() { if (true) { var x = 10; } console.log(x); // 10 - still accessible! } // var is hoisted console.log(y); // undefined (not an error!) var y = 5; // What actually happens (hoisting): // var y; // Declaration moved to top // console.log(y); // undefined // y = 5; // Assignment stays in place

Comparison: let vs const vs var

┌─────────────┬───────────┬───────────┬───────────┐ │ Feature │ let │ const │ var │ ├─────────────┼───────────┼───────────┼───────────┤ │ Scope │ Block │ Block │ Function │ ├─────────────┼───────────┼───────────┼───────────┤ │ Reassign │ Yes │ No │ Yes │ ├─────────────┼───────────┼───────────┼───────────┤ │ Hoisting │ TDZ │ TDZ │ undefined │ ├─────────────┼───────────┼───────────┼───────────┤ │ Redeclare │ No │ No │ Yes │ └─────────────┴───────────┴───────────┴───────────┘ TDZ = Temporal Dead Zone (accessing before declaration throws error)
javascript
// Temporal Dead Zone (TDZ) // console.log(a); // ReferenceError: Cannot access 'a' before initialization let a = 10; // var doesn't have TDZ console.log(b); // undefined var b = 10; // Redeclaration var c = 1; var c = 2; // OK with var let d = 1; // let d = 2; // SyntaxError: Identifier 'd' has already been declared

Best Practices

javascript
// Default to const const API_URL = 'https://api.example.com'; const config = { timeout: 5000 }; // Use let when you need to reassign let count = 0; count++; // Avoid var in modern code // Only use var if you specifically need function scoping (rare)

Primitive Data Types

JavaScript has seven primitive types: string, number, bigint, boolean, undefined, null, and symbol.

Strings

Strings represent textual data.
javascript
// String literals const single = 'Hello'; const double = "World"; const backtick = `Hello, World!`; // Template literals (backticks) const name = 'Alice'; const greeting = `Hello, ${name}!`; // "Hello, Alice!" // Multi-line strings const multiLine = ` This is a multi-line string `; // String properties and methods const str = 'JavaScript'; console.log(str.length); // 10 console.log(str[0]); // 'J' console.log(str.toUpperCase()); // 'JAVASCRIPT' console.log(str.includes('Script')); // true // Strings are immutable let text = 'Hello'; text[0] = 'J'; // Does nothing console.log(text); // 'Hello' text = 'Jello'; // Reassignment works console.log(text); // 'Jello'

Numbers

JavaScript has one number type for both integers and floating-point values.
javascript
// Integers and floats use the same type const integer = 42; const float = 3.14159; const negative = -17; // Scientific notation const big = 1.5e6; // 1,500,000 const small = 2.5e-3; // 0.0025 // Special numeric values const inf = Infinity; const negInf = -Infinity; const notANumber = NaN; // Checking special values console.log(isNaN(NaN)); // true console.log(isFinite(100)); // true console.log(isFinite(Infinity)); // false // Number precision issues console.log(0.1 + 0.2); // 0.30000000000000004 console.log(0.1 + 0.2 === 0.3); // false // Safe integer range console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991 // Number methods console.log((3.14159).toFixed(2)); // "3.14" console.log(parseInt('42px')); // 42 console.log(parseFloat('3.14')); // 3.14 console.log(Number('42')); // 42

BigInt

BigInt handles integers larger than Number.MAX_SAFE_INTEGER.
javascript
// Creating BigInts const big1 = 9007199254740991n; // Literal notation const big2 = BigInt(9007199254740991); // Constructor // Operations const sum = big1 + 1n; console.log(sum); // 9007199254740992n // Cannot mix BigInt and Number // const mixed = big1 + 1; // TypeError // Must convert explicitly const mixed = big1 + BigInt(1); console.log(mixed); // 9007199254740992n // Comparison works with numbers console.log(10n > 5); // true console.log(10n === 10); // false (different types) console.log(10n == 10); // true (with type coercion)

Booleans

Booleans represent logical values: true or false.
javascript
const isActive = true; const isComplete = false; // Boolean operations console.log(!true); // false console.log(true && false); // false console.log(true || false); // true // Comparison results are booleans console.log(5 > 3); // true console.log('a' === 'b'); // false // Boolean conversion console.log(Boolean(1)); // true console.log(Boolean(0)); // false console.log(Boolean('')); // false console.log(Boolean('text')); // true

Undefined and Null

These represent "no value" but have different meanings.
javascript
// undefined - variable declared but not assigned let x; console.log(x); // undefined console.log(typeof x); // "undefined" // null - intentional absence of value let y = null; console.log(y); // null console.log(typeof y); // "object" (this is a historical bug!) // Checking for null console.log(y === null); // true // undefined vs null console.log(undefined == null); // true (loose equality) console.log(undefined === null); // false (strict equality) // Common patterns function findUser(id) { const user = database.get(id); return user || null; // Return null if not found } // Optional parameters function greet(name = 'Guest') { // Default if undefined console.log(`Hello, ${name}!`); } greet(); // "Hello, Guest!" greet(undefined); // "Hello, Guest!" greet(null); // "Hello, null!" (null doesn't trigger default)

Symbols

Symbols are unique, immutable identifiers.
javascript
// Creating symbols const sym1 = Symbol(); const sym2 = Symbol(); console.log(sym1 === sym2); // false (always unique) // Symbols with descriptions const id = Symbol('id'); console.log(id.description); // "id" // Symbols as object keys const user = { name: 'John', [id]: 12345 }; console.log(user.name); // 'John' console.log(user[id]); // 12345 // Symbols are not enumerable console.log(Object.keys(user)); // ['name'] // Well-known symbols const array = [1, 2, 3]; console.log(array[Symbol.iterator]); // function values() { [native code] }

Reference Types

Reference types include objects, arrays, and functions. Unlike primitives, they are stored by reference.

Objects

javascript
// Object literal const person = { name: 'Alice', age: 30, isEmployed: true }; // Accessing properties console.log(person.name); // Dot notation console.log(person['age']); // Bracket notation // Adding/modifying properties person.email = 'alice@example.com'; person.age = 31; // Deleting properties delete person.isEmployed; // Checking properties console.log('name' in person); // true console.log(person.hasOwnProperty('name')); // true // Nested objects const company = { name: 'TechCorp', address: { street: '123 Main St', city: 'Boston' } }; console.log(company.address.city); // 'Boston'

Arrays

javascript
// Array literal const numbers = [1, 2, 3, 4, 5]; // Accessing elements console.log(numbers[0]); // 1 console.log(numbers.length); // 5 // Arrays can hold mixed types const mixed = [1, 'two', true, null, { x: 1 }]; // Common array methods const arr = [1, 2, 3]; arr.push(4); // Add to end: [1, 2, 3, 4] arr.pop(); // Remove from end: [1, 2, 3] arr.unshift(0); // Add to beginning: [0, 1, 2, 3] arr.shift(); // Remove from beginning: [1, 2, 3] // Array destructuring const [first, second] = [1, 2, 3]; console.log(first); // 1 console.log(second); // 2

Functions

javascript
// Function declaration function add(a, b) { return a + b; } // Function expression const subtract = function(a, b) { return a - b; }; // Arrow function const multiply = (a, b) => a * b; // Functions are objects console.log(typeof add); // "function" console.log(add.name); // "add" add.customProperty = 'test'; console.log(add.customProperty); // "test"

Primitives vs References

Understanding the difference between primitive and reference types is crucial.

Primitive Copy Behavior

javascript
// Primitives are copied by value let a = 10; let b = a; // b gets a copy of the value b = 20; console.log(a); // 10 (unchanged) console.log(b); // 20

Reference Copy Behavior

javascript
// Objects are copied by reference let obj1 = { value: 10 }; let obj2 = obj1; // obj2 points to the same object obj2.value = 20; console.log(obj1.value); // 20 (changed!) console.log(obj2.value); // 20 // Arrays behave the same way let arr1 = [1, 2, 3]; let arr2 = arr1; arr2.push(4); console.log(arr1); // [1, 2, 3, 4]

Visual Representation

Primitive Values: ┌─────────┐ let a = 10; a────│ 10 │ └─────────┘ ┌─────────┐ let b = a; b────│ 10 │ (separate copy) └─────────┘ Reference Values: ┌─────────────────┐ let obj1 = {}; obj1───►│ { value: 10 } │ └─────────────────┘ let obj2 = obj1; obj2─────┘ (same object)

Creating True Copies

javascript
// Shallow copy of object const original = { a: 1, b: 2 }; const shallowCopy = { ...original }; shallowCopy.a = 100; console.log(original.a); // 1 (unchanged) // Shallow copy of array const originalArr = [1, 2, 3]; const arrayCopy = [...originalArr]; // Deep copy (for nested objects) const nested = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(nested)); // Modern deep copy const modernDeepCopy = structuredClone(nested);

Type Coercion

JavaScript automatically converts types in certain situations.

Implicit Coercion

javascript
// String coercion with + console.log('5' + 3); // '53' (number to string) console.log('5' + true); // '5true' // Numeric coercion with other operators console.log('5' - 3); // 2 (string to number) console.log('5' * '2'); // 10 console.log('10' / '2'); // 5 // Boolean coercion if ('hello') { // Non-empty string is truthy console.log('truthy'); } if (0) { // 0 is falsy console.log('never runs'); }

Truthy and Falsy Values

javascript
// Falsy values (convert to false) Boolean(false); // false Boolean(0); // false Boolean(-0); // false Boolean(0n); // false (BigInt zero) Boolean(''); // false Boolean(null); // false Boolean(undefined); // false Boolean(NaN); // false // Everything else is truthy Boolean(true); // true Boolean(1); // true Boolean('0'); // true (non-empty string!) Boolean('false'); // true (non-empty string!) Boolean([]); // true (empty array!) Boolean({}); // true (empty object!) Boolean(function(){}); // true

Explicit Coercion

javascript
// To String String(123); // "123" (123).toString(); // "123" 123 + ''; // "123" // To Number Number('123'); // 123 parseInt('123px'); // 123 parseFloat('3.14'); // 3.14 +'123'; // 123 (unary plus) // To Boolean Boolean(1); // true !!1; // true (double NOT)

Equality Comparisons

javascript
// Loose equality (==) - allows coercion console.log(5 == '5'); // true console.log(0 == false); // true console.log(null == undefined); // true console.log('' == 0); // true // Strict equality (===) - no coercion console.log(5 === '5'); // false console.log(0 === false); // false console.log(null === undefined); // false // Always prefer strict equality const value = '42'; if (value === 42) { console.log('This will not run'); } if (value === '42') { console.log('This will run'); }

The typeof Operator

javascript
console.log(typeof 'hello'); // "string" console.log(typeof 42); // "number" console.log(typeof 42n); // "bigint" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof Symbol()); // "symbol" console.log(typeof null); // "object" (bug!) console.log(typeof {}); // "object" console.log(typeof []); // "object" console.log(typeof function(){}); // "function" // Better type checking console.log(Array.isArray([])); // true console.log(value === null); // for null check console.log(value instanceof Date); // for built-in types

Summary

Variables in JavaScript:
  • Use const by default for values that won't be reassigned
  • Use let when reassignment is needed
  • Avoid var in modern code
  • const prevents reassignment but not mutation
Primitive types:
  • string, number, bigint, boolean, undefined, null, symbol
  • Immutable and copied by value
  • Compared by value
Reference types:
  • Objects, arrays, functions
  • Mutable and copied by reference
  • Compared by reference
Type coercion:
  • JavaScript automatically converts types when needed
  • Use strict equality (===) to avoid unexpected coercion
  • Understand truthy and falsy values

Questions to Think About

  1. Why should you prefer const over let?
Using const by default signals your intent that the binding won't change. It makes code easier to reason about because you know the variable always refers to the same value. Use let only when you truly need to reassign.
  1. Why does typeof null return "object"?
This is a historical bug from the first version of JavaScript. The type tag for objects was 0, and null was represented as the NULL pointer (0x00), so its type tag was also 0. It can't be fixed now without breaking existing code.
  1. Why are 0.1 + 0.2 !== 0.3?
JavaScript (and most languages) use IEEE 754 floating-point representation, which cannot precisely represent some decimal fractions. 0.1 and 0.2 are stored as approximations, and their sum is slightly off from 0.3.
  1. When would you use Symbol?
Symbols are useful for creating unique property keys that won't conflict with other properties, implementing protocols like iterators, and creating "private" properties (though not truly private).
  1. What's the difference between null and undefined?
undefined means a variable has been declared but not assigned a value. null is an intentional assignment indicating "no value." Use null when you want to explicitly indicate emptiness; let undefined be the default for uninitialized values.

Next: Part 3 - Operators and Expressions, where we explore arithmetic, comparison, logical, and bitwise operators.
All Blogs
Tags:javascriptvariablesdata-typesprimitivescoercion