JavaScript Tutorial

JavaScript Data Types: Primitives and Reference Types Explained

Master JavaScript data types including primitives (String, Number, Boolean, Null, Undefined, Symbol, BigInt) and reference types (Objects, Arrays). Learn type checking, conversion, and best practices.

Welcome back! 👋 In the previous lesson, you learned how to create variables to store data. But what kind of data can you actually store? That's what we'll explore in this lesson!

Data types define what kind of value a variable can hold and what operations you can perform on it. JavaScript has two main categories of data types: Primitive Types and Reference Types.

Understanding data types is fundamental to writing effective JavaScript code. Let's dive in!


What are Data Types?

A data type is a classification that specifies what kind of value a variable can hold and how that value is stored in memory.

Why Data Types Matter

Different types of data:

  • Are stored differently in memory
  • Support different operations
  • Behave differently when copied or compared

Example:

let age = 25;              // Number - can do math
let name = "Mihir";        // String - can't do math
let isStudent = true;      // Boolean - true or false only

console.log(age + 10);     // 35 (math works)
console.log(name + 10);    // "Mihir10" (concatenation, not math)
console.log(isStudent + 1); // 2 (true converts to 1)

JavaScript's Type System

JavaScript is a dynamically typed (or loosely typed) language, meaning:

  • Variables can hold any type of data
  • Types are determined at runtime
  • Same variable can hold different types at different times
let data = 42;           // Number
console.log(typeof data); // "number"

data = "Hello";          // Now a String
console.log(typeof data); // "string"

data = true;             // Now a Boolean
console.log(typeof data); // "boolean"

Compare with statically typed languages (like Java, C++):

// Java - must declare type
int age = 25;
age = "John";  // ❌ ERROR: Cannot assign String to int

JavaScript is more flexible but requires careful attention to types!


The Two Categories of Data Types

JavaScript has 8 data types divided into two categories:

1. Primitive Types (7 types)

  • Stored directly in the variable's memory location
  • Immutable (cannot be changed)
  • Copied by value

The 7 Primitive Types:

  1. String - Text data
  2. Number - Numeric data (integers and decimals)
  3. Boolean - true or false
  4. Undefined - Variable declared but not assigned
  5. Null - Intentional absence of value
  6. Symbol - Unique identifiers (ES6)
  7. BigInt - Large integers (ES2020)

2. Reference Types (1 type with subtypes)

  • Stored as references (pointers) to memory locations
  • Mutable (can be changed)
  • Copied by reference

Reference Type:

  1. Object - Collections of key-value pairs
    • Plain objects: { name: "John" }
    • Arrays: [1, 2, 3]
    • Functions: function() {}
    • Dates: new Date()
    • RegExp: /pattern/
    • And more...

Primitive Data Types

Let's explore each primitive type in detail.


1. String

A string is a sequence of characters used to represent text.

Creating Strings

// Single quotes
let firstName = 'John';

// Double quotes
let lastName = "Doe";

// Template literals (backticks) - ES6
let fullName = `John Doe`;

// All three are valid and create strings

String Operations

let greeting = "Hello";
let name = "Mihir";

// Concatenation with +
let message = greeting + " " + name;
console.log(message); // "Hello Mihir"

// Template literals (recommended for complex strings)
let message2 = `${greeting} ${name}!`;
console.log(message2); // "Hello Mihir!"

// String length
console.log(name.length); // 5

// Accessing characters (zero-indexed)
console.log(name[0]); // "M"
console.log(name[4]); // "r"

// Strings are immutable
name[0] = "P"; // Does nothing
console.log(name); // Still "Mihir"

Common String Methods

let text = "JavaScript is awesome";

// Convert to uppercase
console.log(text.toUpperCase()); // "JAVASCRIPT IS AWESOME"

// Convert to lowercase
console.log(text.toLowerCase()); // "javascript is awesome"

// Check if string contains substring
console.log(text.includes("Java")); // true
console.log(text.includes("Python")); // false

// Find position of substring
console.log(text.indexOf("is")); // 11
console.log(text.indexOf("xyz")); // -1 (not found)

// Extract substring
console.log(text.slice(0, 10)); // "JavaScript"
console.log(text.substring(0, 10)); // "JavaScript"

// Replace text
console.log(text.replace("awesome", "powerful")); // "JavaScript is powerful"

// Split into array
console.log(text.split(" ")); // ["JavaScript", "is", "awesome"]

// Trim whitespace
let messy = "  hello  ";
console.log(messy.trim()); // "hello"

Escape Characters

// Special characters need escaping
let quote = "He said, \"Hello!\"";        // He said, "Hello!"
let path = "C:\\Users\\Mihir\\Documents"; // C:\Users\Mihir\Documents
let multiline = "Line 1\nLine 2\nLine 3"; // Line 1
                                          // Line 2
                                          // Line 3

// Template literals for multiline (easier)
let multiline2 = `Line 1
Line 2
Line 3`;

String Type Checking

let name = "Mihir";
console.log(typeof name); // "string"

2. Number

JavaScript has only one number type for both integers and floating-point numbers.

Creating Numbers

let age = 30;                 // Integer
let price = 19.99;            // Decimal
let negative = -15;           // Negative
let scientific = 2.5e6;       // Scientific notation (2.5 × 10⁶ = 2,500,000)
let hex = 0xFF;               // Hexadecimal (255)
let binary = 0b1010;          // Binary (10)
let octal = 0o17;             // Octal (15)

Number Operations

let a = 10;
let b = 3;

// Basic arithmetic
console.log(a + b);  // 13 - Addition
console.log(a - b);  // 7  - Subtraction
console.log(a * b);  // 30 - Multiplication
console.log(a / b);  // 3.333... - Division
console.log(a % b);  // 1  - Modulus (remainder)
console.log(a ** b); // 1000 - Exponentiation (10³)

// Increment and decrement
let count = 5;
count++;             // 6 - Increment by 1
count--;             // 5 - Decrement by 1

Special Number Values

// Infinity
console.log(1 / 0);           // Infinity
console.log(-1 / 0);          // -Infinity
console.log(Infinity + 1);    // Infinity

// NaN (Not a Number)
console.log("hello" * 2);     // NaN
console.log(Math.sqrt(-1));   // NaN
console.log(0 / 0);           // NaN

// Checking for NaN (NaN is special - it's not equal to itself!)
console.log(NaN === NaN);     // false (weird but true!)
console.log(isNaN(NaN));      // true (use this instead)
console.log(Number.isNaN(NaN)); // true (more reliable)

Common Number Methods

let num = 42.7845;

// Rounding
console.log(Math.round(num));  // 43 - Round to nearest integer
console.log(Math.floor(num));  // 42 - Round down
console.log(Math.ceil(num));   // 43 - Round up
console.log(num.toFixed(2));   // "42.78" - Fixed decimal places (returns string!)

// Convert string to number
let str = "123";
console.log(Number(str));      // 123
console.log(parseInt(str));    // 123
console.log(parseFloat("3.14")); // 3.14

// Check if value is finite
console.log(Number.isFinite(42));       // true
console.log(Number.isFinite(Infinity)); // false

// Random numbers
console.log(Math.random());           // Random between 0 and 1
console.log(Math.random() * 10);      // Random between 0 and 10
console.log(Math.floor(Math.random() * 100)); // Random integer 0-99

Number Precision Issues

Important: JavaScript uses floating-point arithmetic which can cause precision issues:

console.log(0.1 + 0.2);           // 0.30000000000000004 (not exactly 0.3!)
console.log(0.1 + 0.2 === 0.3);   // false

// Solution: Round to fixed decimals or use integers
let result = (0.1 + 0.2).toFixed(2);
console.log(result);               // "0.30" (string)
console.log(Number(result));       // 0.3 (number)

// Or work with integers and divide later
let cents1 = 10; // $0.10
let cents2 = 20; // $0.20
let totalCents = cents1 + cents2;
let dollars = totalCents / 100;
console.log(dollars); // 0.3

Number Type Checking

let age = 30;
console.log(typeof age); // "number"

// Check if it's actually a number (not NaN)
console.log(typeof NaN);        // "number" (confusing!)
console.log(Number.isNaN(NaN)); // true
console.log(Number.isFinite(age)); // true (not NaN or Infinity)

3. Boolean

A boolean represents a logical value: true or false.

Creating Booleans

let isLoggedIn = true;
let hasPermission = false;
let isActive = true;

Boolean in Conditions

let age = 18;
let isAdult = age >= 18;
console.log(isAdult); // true

if (isAdult) {
  console.log("You are an adult");
} else {
  console.log("You are a minor");
}

Truthy and Falsy Values

In JavaScript, all values have an inherent boolean value called truthy or falsy.

Falsy values (only 6):

false         // Boolean false
0             // Number zero
-0            // Negative zero
""            // Empty string
null          // Null value
undefined     // Undefined value
NaN           // Not a Number

// All falsy values convert to false
if (0) {
  console.log("Won't run"); // Doesn't execute
}

Truthy values (everything else):

true          // Boolean true
1, 42, -5     // Any non-zero number
"hello"       // Any non-empty string
" "           // Even string with just space
[]            // Empty array (surprising!)
{}            // Empty object
function(){}  // Functions

// All truthy values convert to true
if (1) {
  console.log("Will run"); // Executes
}

if ("hello") {
  console.log("Will run"); // Executes
}

Boolean Operations

let a = true;
let b = false;

// Logical AND (&&)
console.log(a && b);  // false (both must be true)
console.log(a && a);  // true

// Logical OR (||)
console.log(a || b);  // true (at least one must be true)
console.log(b || b);  // false

// Logical NOT (!)
console.log(!a);      // false (inverts value)
console.log(!b);      // true
console.log(!!a);     // true (double negation - converts to boolean)

Converting to Boolean

// Explicit conversion
console.log(Boolean(1));        // true
console.log(Boolean(0));        // false
console.log(Boolean("hello"));  // true
console.log(Boolean(""));       // false

// Implicit conversion (double NOT)
console.log(!!"hello");         // true
console.log(!!0);               // false

// In conditions (automatic conversion)
let value = "hello";
if (value) {
  console.log("Truthy!"); // Executes because "hello" is truthy
}

Boolean Type Checking

let flag = true;
console.log(typeof flag); // "boolean"

4. Undefined

Undefined means a variable has been declared but has not been assigned a value.

When You Get Undefined

// 1. Variable declared but not initialized
let name;
console.log(name); // undefined

// 2. Function with no return statement
function greet() {
  console.log("Hello");
}
let result = greet(); // Prints "Hello"
console.log(result);  // undefined

// 3. Accessing non-existent object property
let person = { name: "John" };
console.log(person.age); // undefined

// 4. Function parameter not provided
function sayHello(name) {
  console.log(name);
}
sayHello(); // undefined

Undefined vs Not Declared

let declared;
console.log(declared);    // undefined (variable exists)

console.log(notDeclared); // ❌ ReferenceError (variable doesn't exist)

Checking for Undefined

let value;

// Method 1: Direct comparison
if (value === undefined) {
  console.log("Value is undefined");
}

// Method 2: typeof
if (typeof value === "undefined") {
  console.log("Value is undefined");
}

// Method 3: Falsy check (but also catches null, 0, "", etc.)
if (!value) {
  console.log("Value is falsy");
}

Type Checking

let x;
console.log(typeof x); // "undefined"

5. Null

Null represents the intentional absence of any value. It's explicitly assigned to indicate "no value" or "empty".

Creating Null

let user = null;  // Intentionally empty
let data = null;  // Waiting for data

Undefined vs Null

Both represent "no value", but with different meanings:

let notAssigned;           // undefined - hasn't been set yet
let intentionallyEmpty = null; // null - explicitly set to "no value"

// undefined: "I don't know yet"
// null: "I know there's nothing here"

Example: User data

let userName = undefined;  // User data not loaded yet
let userName = null;       // User has no name (intentionally empty)

Type Checking (Gotcha!)

let value = null;
console.log(typeof value); // "object" ⚠️ This is a JavaScript bug!

// To properly check for null:
console.log(value === null); // true (correct way)

Null vs Undefined Comparison

console.log(null == undefined);  // true (loose equality)
console.log(null === undefined); // false (strict equality - different types)

console.log(typeof null);        // "object" (bug in JavaScript)
console.log(typeof undefined);   // "undefined"

6. Symbol (ES6)

Symbol is a unique and immutable primitive value, often used as object property keys to avoid naming conflicts.

Creating Symbols

// Each symbol is unique
let sym1 = Symbol();
let sym2 = Symbol();

console.log(sym1 === sym2); // false (always unique!)

// Symbols with description (for debugging)
let id = Symbol("id");
let id2 = Symbol("id");

console.log(id === id2); // false (still unique despite same description)
console.log(id.description); // "id"

Use Case: Unique Object Properties

let id = Symbol("id");

let user = {
  name: "John",
  [id]: 123  // Symbol as property key
};

console.log(user.name);  // "John"
console.log(user[id]);   // 123
console.log(user.id);    // undefined (not the same as Symbol!)

// Symbols don't show in normal iteration
console.log(Object.keys(user)); // ["name"] - Symbol not included

Type Checking

let sym = Symbol("test");
console.log(typeof sym); // "symbol"

Note: Symbols are advanced and rarely used by beginners. You'll encounter them more in framework internals.


7. BigInt (ES2020)

BigInt is used for integers larger than Number.MAX_SAFE_INTEGER (2⁵³ - 1).

Number Limitations

// Regular numbers have limits
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991

let big = 9007199254740991;
console.log(big + 1); // 9007199254740992 ✅
console.log(big + 2); // 9007199254740992 ❌ Wrong! (lost precision)

Creating BigInt

// Add 'n' suffix
let bigInt1 = 1234567890123456789012345678901234567890n;

// Using BigInt constructor
let bigInt2 = BigInt("1234567890123456789012345678901234567890");

// Operations
let a = 10n;
let b = 20n;
console.log(a + b); // 30n
console.log(a * b); // 200n

BigInt Restrictions

let bigNum = 10n;
let normalNum = 10;

// ❌ Cannot mix BigInt and Number
console.log(bigNum + normalNum); // TypeError

// ✅ Must convert first
console.log(bigNum + BigInt(normalNum)); // 20n
console.log(Number(bigNum) + normalNum); // 20

Type Checking

let big = 123n;
console.log(typeof big); // "bigint"

Note: BigInt is for specialized use cases (cryptography, timestamps, large calculations). Most JavaScript programs don't need it.


Reference Types (Objects)

Unlike primitives which store values directly, reference types store references (memory addresses) to values.


Objects

An object is a collection of key-value pairs (properties).

Creating Objects

// Object literal (most common)
let person = {
  name: "Mihir",
  age: 30,
  isStudent: false,
  city: "Mumbai"
};

// Empty object
let emptyObj = {};

// Using Object constructor (less common)
let person2 = new Object();
person2.name = "John";

Accessing Object Properties

let person = {
  name: "Mihir",
  age: 30,
  "favorite color": "blue"  // Property with space needs quotes
};

// Dot notation (preferred)
console.log(person.name);  // "Mihir"
console.log(person.age);   // 30

// Bracket notation (for special keys or variables)
console.log(person["name"]);           // "Mihir"
console.log(person["favorite color"]); // "blue" (space in key)

let key = "age";
console.log(person[key]); // 30 (dynamic access)

Modifying Objects

let person = {
  name: "Mihir",
  age: 30
};

// Update existing property
person.age = 31;
console.log(person.age); // 31

// Add new property
person.city = "Mumbai";
console.log(person.city); // "Mumbai"

// Delete property
delete person.age;
console.log(person.age); // undefined

Nested Objects

let user = {
  name: "Mihir",
  address: {
    street: "123 Main St",
    city: "Mumbai",
    country: "India"
  },
  contact: {
    email: "mihir@example.com",
    phone: "123-456-7890"
  }
};

// Accessing nested properties
console.log(user.address.city);        // "Mumbai"
console.log(user.contact.email);       // "mihir@example.com"

Objects are Mutable

const person = {
  name: "John",
  age: 30
};

// ✅ Can modify properties (even with const)
person.age = 31;
person.city = "NYC";
console.log(person); // { name: "John", age: 31, city: "NYC" }

// ❌ Cannot reassign entire object
person = { name: "Jane" }; // TypeError: Assignment to constant variable

Arrays

An array is an ordered list of values. Arrays are actually special types of objects.

Creating Arrays

// Array literal (most common)
let numbers = [1, 2, 3, 4, 5];
let fruits = ["apple", "banana", "orange"];
let mixed = [1, "hello", true, null, { name: "John" }];

// Empty array
let empty = [];

// Using Array constructor (less common)
let arr = new Array(3); // Creates array with 3 empty slots

Accessing Array Elements

let fruits = ["apple", "banana", "orange"];

// Zero-indexed
console.log(fruits[0]); // "apple"
console.log(fruits[1]); // "banana"
console.log(fruits[2]); // "orange"
console.log(fruits[3]); // undefined (doesn't exist)

// Array length
console.log(fruits.length); // 3

// Last element
console.log(fruits[fruits.length - 1]); // "orange"

Modifying Arrays

let colors = ["red", "green", "blue"];

// Update element
colors[1] = "yellow";
console.log(colors); // ["red", "yellow", "blue"]

// Add to end
colors.push("purple");
console.log(colors); // ["red", "yellow", "blue", "purple"]

// Remove from end
let last = colors.pop();
console.log(last);    // "purple"
console.log(colors);  // ["red", "yellow", "blue"]

// Add to beginning
colors.unshift("pink");
console.log(colors); // ["pink", "red", "yellow", "blue"]

// Remove from beginning
let first = colors.shift();
console.log(first);   // "pink"
console.log(colors);  // ["red", "yellow", "blue"]

Common Array Methods

let numbers = [1, 2, 3, 4, 5];

// Find element
console.log(numbers.includes(3)); // true
console.log(numbers.indexOf(4));  // 3 (index position)

// Join elements
console.log(numbers.join("-")); // "1-2-3-4-5"

// Slice (extract portion)
console.log(numbers.slice(1, 4)); // [2, 3, 4]

// Reverse
let reversed = numbers.reverse();
console.log(reversed); // [5, 4, 3, 2, 1]

// Sort
let unsorted = [3, 1, 4, 1, 5];
console.log(unsorted.sort()); // [1, 1, 3, 4, 5]

Arrays are Objects

let arr = [1, 2, 3];
console.log(typeof arr); // "object" (arrays are objects!)

// To check if it's actually an array:
console.log(Array.isArray(arr)); // true
console.log(Array.isArray({}));  // false

Primitive vs Reference Types: Key Differences

Understanding the difference between primitives and references is crucial!

1. Storage

Primitives: Stored by Value

let a = 10;
let b = a;  // Copies the VALUE

b = 20;     // Changing b doesn't affect a

console.log(a); // 10 (unchanged)
console.log(b); // 20

References: Stored by Reference

let person1 = { name: "John" };
let person2 = person1;  // Copies the REFERENCE (not the object)

person2.name = "Jane";  // Changes the SAME object

console.log(person1.name); // "Jane" (changed!)
console.log(person2.name); // "Jane"

2. Comparison

Primitives: Compare Values

let a = 10;
let b = 10;
console.log(a === b); // true (same value)

let str1 = "hello";
let str2 = "hello";
console.log(str1 === str2); // true (same value)

References: Compare References

let obj1 = { name: "John" };
let obj2 = { name: "John" };
console.log(obj1 === obj2); // false (different references!)

let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false (different references!)

// Same reference
let obj3 = obj1;
console.log(obj1 === obj3); // true (same reference)

3. Mutability

Primitives: Immutable

let str = "hello";
str[0] = "H";        // Does nothing
console.log(str);    // "hello" (unchanged)

str = str.toUpperCase(); // Creates NEW string
console.log(str);    // "HELLO"

References: Mutable

let person = { name: "John" };
person.name = "Jane"; // Modifies the object directly
console.log(person);  // { name: "Jane" }

let arr = [1, 2, 3];
arr.push(4);          // Modifies the array directly
console.log(arr);     // [1, 2, 3, 4]

Visual Comparison

PRIMITIVES                   REFERENCES
──────────                   ──────────
Stack Memory                 StackHeap Memory
┌──────────┐                ┌──────────┐      ┌──────────────┐
│ a = 10   │                │ obj1 ──→ │      │ {name:"John"}│
└──────────┘                └──────────┘      └──────────────┘
┌──────────┐                ┌──────────┐             ↑
│ b = 10   │                │ obj2 ──→ │ ────────────┘
└──────────┘                └──────────┘
(separate values)           (same reference)

Type Checking with typeof

The typeof operator returns a string indicating the type of a value.

Syntax

typeof value
// or
typeof(value)

Examples

// Primitives
console.log(typeof "hello");     // "string"
console.log(typeof 42);          // "number"
console.log(typeof true);        // "boolean"
console.log(typeof undefined);   // "undefined"
console.log(typeof Symbol());    // "symbol"
console.log(typeof 123n);        // "bigint"

// References
console.log(typeof {});          // "object"
console.log(typeof []);          // "object" ⚠️
console.log(typeof null);        // "object" ⚠️ (JavaScript bug)
console.log(typeof function(){}); // "function" ⚠️ (special case)

typeof Quirks

Problem 1: Arrays return "object"

let arr = [1, 2, 3];
console.log(typeof arr); // "object"

// Solution: Use Array.isArray()
console.log(Array.isArray(arr)); // true

Problem 2: null returns "object"

let value = null;
console.log(typeof value); // "object" (bug in JavaScript!)

// Solution: Check explicitly
console.log(value === null); // true

Problem 3: Functions return "function"

function greet() {}
console.log(typeof greet); // "function"

// Functions are actually objects!

Proper Type Checking

// Check for array
function isArray(value) {
  return Array.isArray(value);
}

// Check for null
function isNull(value) {
  return value === null;
}

// Check for object (not array or null)
function isObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

// Check for number (not NaN)
function isNumber(value) {
  return typeof value === "number" && !isNaN(value);
}

Type Conversion (Type Casting)

Converting one data type to another is called type conversion or type casting.

Implicit Conversion (Type Coercion)

JavaScript automatically converts types when needed:

// String + Number = String (concatenation)
console.log("5" + 3);        // "53"
console.log("Hello" + 5);    // "Hello5"

// String - Number = Number (subtraction)
console.log("5" - 3);        // 2
console.log("10" / "2");     // 5

// Boolean to Number
console.log(true + 1);       // 2 (true becomes 1)
console.log(false + 1);      // 1 (false becomes 0)

// String to Number in comparison
console.log("5" > 3);        // true ("5" becomes 5)

Explicit Conversion

To String:

let num = 123;

// Method 1: String()
console.log(String(num));      // "123"

// Method 2: toString()
console.log(num.toString());   // "123"

// Method 3: Concatenation
console.log(num + "");         // "123"

// Method 4: Template literal
console.log(`${num}`);         // "123"

To Number:

let str = "123";

// Method 1: Number()
console.log(Number(str));      // 123
console.log(Number("3.14"));   // 3.14
console.log(Number("abc"));    // NaN

// Method 2: parseInt() and parseFloat()
console.log(parseInt("123"));      // 123
console.log(parseInt("123.45"));   // 123 (only integer part)
console.log(parseFloat("123.45")); // 123.45

// Method 3: Unary plus
console.log(+str);             // 123
console.log(+"3.14");          // 3.14

To Boolean:

// Method 1: Boolean()
console.log(Boolean(1));           // true
console.log(Boolean(0));           // false
console.log(Boolean("hello"));     // true
console.log(Boolean(""));          // false

// Method 2: Double NOT
console.log(!!"hello");            // true
console.log(!!0);                  // false

Conversion Table

Original Valueto Stringto Numberto Boolean
undefined"undefined"NaNfalse
null"null"0false
true"true"1true
false"false"0false
"" (empty)""0false
"0""0"0true
"123""123"123true
"abc""abc"NaNtrue
0"0"0false
123"123"123true
[]""0true
[1,2]"1,2"NaNtrue
{}"[object Object]"NaNtrue

Common Type-Related Mistakes

Mistake 1: Concatenation Instead of Addition

// ❌ Problem
let a = "5";
let b = 3;
console.log(a + b); // "53" (concatenation, not addition!)

// ✅ Solution
console.log(Number(a) + b); // 8
console.log(+a + b);        // 8

Mistake 2: Comparing Different Types

// ❌ Loose equality (==) converts types
console.log("5" == 5);   // true (converted to same type)
console.log(0 == false); // true

// ✅ Strict equality (===) checks type AND value
console.log("5" === 5);   // false (different types)
console.log(0 === false); // false

Mistake 3: Treating Arrays as Primitives

// ❌ Problem
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false (different references!)

// ✅ Solution: Compare values manually
function arraysEqual(a, b) {
  return JSON.stringify(a) === JSON.stringify(b);
}
console.log(arraysEqual(arr1, arr2)); // true

Mistake 4: NaN Comparisons

// ❌ Problem
let result = "abc" * 2; // NaN
console.log(result === NaN); // false (NaN !== NaN!)

// ✅ Solution
console.log(isNaN(result));        // true
console.log(Number.isNaN(result)); // true (more reliable)

Practical Exercise: Type Exploration

Create a file called types-demo.js:

// Type Exploration Demo

// Function to check and display type information
function analyzeType(value, name) {
  console.log(`\n=== ${name} ===`);
  console.log("Value:", value);
  console.log("Type:", typeof value);
  console.log("Truthy?", !!value);
  
  if (typeof value === "object") {
    console.log("Is Array?", Array.isArray(value));
    console.log("Is null?", value === null);
  }
}

// Test different types
analyzeType("Hello", "String");
analyzeType(42, "Number");
analyzeType(true, "Boolean");
analyzeType(undefined, "Undefined");
analyzeType(null, "Null");
analyzeType([1, 2, 3], "Array");
analyzeType({ name: "John" }, "Object");
analyzeType(Symbol("id"), "Symbol");
analyzeType(123n, "BigInt");

// Type conversion examples
console.log("\n=== Type Conversions ===");
console.log("String to Number: '123' →", Number("123"));
console.log("Number to String: 123 →", String(123));
console.log("String to Boolean: 'hello' →", Boolean("hello"));
console.log("Number to Boolean: 0 →", Boolean(0));

// Primitive vs Reference
console.log("\n=== Primitive vs Reference ===");

let num1 = 10;
let num2 = num1;
num2 = 20;
console.log("Primitive - num1:", num1, "num2:", num2);

let obj1 = { value: 10 };
let obj2 = obj1;
obj2.value = 20;
console.log("Reference - obj1:", obj1, "obj2:", obj2);

Run it:

node types-demo.js

Key Takeaways

Congratulations! You now understand JavaScript data types. Here's what you learned:

JavaScript has 8 data types: 7 primitives + 1 reference type (Object)

Primitive Types:

  • String - Text data with quotes
  • Number - Numeric values (integers and decimals)
  • Boolean - true or false
  • Undefined - Variable declared but not assigned
  • Null - Intentional absence of value
  • Symbol - Unique identifiers
  • BigInt - Large integers

Reference Types:

  • Object - Collections of key-value pairs
  • Array - Ordered lists (special type of object)

Key Differences:

  • Primitives: Stored by value, immutable
  • References: Stored by reference, mutable

Type Checking:

  • Use typeof for most types
  • Use Array.isArray() for arrays
  • Use === null for null

Type Conversion:

  • Implicit (automatic by JavaScript)
  • Explicit (manual with String(), Number(), Boolean())

Best Practices

  1. ✅ Use === instead of == (checks type and value)
  2. ✅ Convert types explicitly when needed
  3. ✅ Check types before operations
  4. ✅ Use Array.isArray() to check for arrays
  5. ✅ Use Number.isNaN() instead of isNaN()
  6. ✅ Be aware of truthy/falsy values
  7. ✅ Remember: arrays and null have typeof "object"
  8. ✅ Use descriptive variable names that indicate type

What's Next?

Excellent work! 🎉 You now understand JavaScript's data type system.

In the next lesson, we'll explore Type Conversion & Coercion in greater depth, covering:

  • Explicit vs implicit conversion
  • Common coercion pitfalls
  • Best practices for type handling
  • Comparison operators and type coercion

Understanding data types is foundational - you'll use this knowledge in every JavaScript program you write!