JavaScript Core Syntax & Types ★ Primary Guide

Mastering JavaScript Core Syntax and Data Types

⏱ 12 min read • Level: Beginner • Updated: Sep 30, 2026

Introduction: The Engine Behind Modern JavaScript

JavaScript is a dynamically typed, garbage-collected programming language that powers both client-side interfaces and server-side runtimes. While modern frameworks abstract much of the raw syntax, a senior engineer’s ability to debug subtle production bugs hinges on an exact understanding of foundational mechanics: how variables are stored in memory, how the runtime determines equality, and how lexical environments evaluate variable access.

In this guide, you will master the mental models governing JavaScript’s data representation, the distinctions between value passing and reference mutations, the behavior of variable declarations under hoisting, and the precise coercion algorithms executed during comparisons.

Mental Model: Memory Layout & Values

The JavaScript virtual machine (such as V8) manages memory using two primary regions: the Stack and the Heap.

  • The Stack: Fast, contiguous memory allocated for execution context frames and fixed-size values. Primitive types (with small, fixed memory bounds) are stored directly on the stack or tracked by reference pointers.
  • The Heap: A large, unstructured memory pool allocated dynamically for composite entities whose size cannot be predicted at compile time, such as Objects, Arrays, and Functions.

1. Primitive vs. Reference Types

JavaScript categorizes values into two distinct families: seven primitive data types and one composite object type.

The 7 Primitive Types

Primitives are immutable values represented directly at the lowest level of the language:

  1. number: Double-precision 64-bit binary format IEEE 754 values (including Infinity, -Infinity, and NaN).
  2. string: Sequences of 16-bit unsigned integers representing UTF-16 code units.
  3. boolean: Logical entities with values true or false.
  4. undefined: The default value assigned automatically to uninitialized variables or missing object properties.
  5. null: An intentional assignment representing the complete absence of any object value.
  6. symbol: Unique and immutable primitive identifiers introduced in ES6, often used as non-enumerable object keys.
  7. bigint: Arbitrary-precision integers capable of exceeding the safe integer limit (Number.MAX_SAFE_INTEGER).

Reference Types (Objects)

Every non-primitive value in JavaScript is an Object (including standard objects, arrays, functions, dates, and maps). When assigning an object to a variable, the variable does not hold the object’s properties directly; instead, it holds a reference (pointer) to the object’s heap address.

// Value Semantics (Primitives)
let a = 10;
let b = a; // b receives a copy of the primitive value 10
b = 25;
console.log(a); // 10 (unchanged)

// Reference Semantics (Objects)
const original = { name: "SkillCertify", score: 95 };
const copy = original; // copy receives the same memory reference
copy.score = 100;
console.log(original.score); // 100 (mutated through shared reference!)

2. Variable Declarations: var, let, and const

Prior to ECMAScript 2015 (ES6), variables were exclusively declared using var. Modern production standards mandate block-scoped declarations via let and const.

Keyword Scope Hoisting Behavior Reassignment Redeclaration
var Function / Global Initialized to undefined Allowed Allowed
let Block {} Hoisted into Temporal Dead Zone (TDZ) Allowed Syntax Error
const Block {} Hoisted into Temporal Dead Zone (TDZ) Type Error Syntax Error

Hoisting and the Temporal Dead Zone (TDZ)

Hoisting is the process during the compilation phase where the JavaScript engine registers variable and function declarations into memory before executing any code statements.

While var variables are hoisted and immediately initialized to undefined, let and const declarations are hoisted but remain uninitialized until their declaration statement is reached during evaluation. The window of execution between scope entry and declaration is known as the Temporal Dead Zone (TDZ). Attempting to read or write to a TDZ variable throws a ReferenceError.

// var hoisting:
console.log(legacyVar); // undefined (hoisted & initialized)
var legacyVar = "Ready";

// let / const hoisting:
try {
    console.log(modernLet); // ReferenceError: Cannot access 'modernLet' before initialization
    let modernLet = "Accessible";
} catch (err) {
    console.error(err.message);
}

3. Type Coercion & Equality Mechanics

Type coercion is the automatic or explicit conversion of values from one data type to another. Understanding coercion is essential for writing predictable conditions and unit tests.

Truthy vs. Falsy Values

When evaluated in a boolean context (such as an if condition or ternary statement), JavaScript coerces all values to either true or false. Exactly eight values evaluate to falsy in ECMAScript:

  • false
  • 0, -0, and 0n (BigInt zero)
  • "" (empty string)
  • null
  • undefined
  • NaN (Not-a-Number)

Crucial Rule: All other values are truthy, including empty objects {}, empty arrays [], and string zeros "0".

Loose Equality (==) vs. Strict Equality (===)

The Abstract Equality Comparison Algorithm (==) performs automatic type conversion before comparing values. The Strict Equality operator (===) compares both value and type without coercion.

// Loose Equality (Performs Coercion)
console.log("42" == 42);            // true (string converts to number)
console.log(null == undefined);     // true (special specification rule)
console.log(0 == false);            // true (boolean converts to number 0)
console.log([] == false);           // true (array converts to primitive "" -> 0)

// Strict Equality (No Coercion - Recommended Standard)
console.log("42" === 42);           // false (types differ: string vs number)
console.log(null === undefined);    // false
console.log(0 === false);           // false
console.log([] === false);          // false

4. Common Pitfalls and Edge Cases

The typeof null Historical Artifact

Evaluating typeof null returns "object". This is a notorious legacy bug from the very first implementation of JavaScript in 1995, where values were represented with type tags and objects had a tag of 0. Since null was represented as a NULL pointer (0x00), it inherited the object tag. To reliably check for null, always use strict equality:

const value = null;
console.log(typeof value === "object"); // true (misleading!)
console.log(value === null);           // true (accurate check)

The Peculiarities of NaN

NaN (Not a Number) is the only value in JavaScript that is not equal to itself: NaN === NaN evaluates to false. To check for NaN, use Number.isNaN(val) instead of the global isNaN(), as the global function coerces non-numeric types prior to testing.

IEEE 754 Floating-Point Math

Like most modern languages, JavaScript numbers are IEEE 754 double-precision floats. Binary representations cannot accurately represent certain decimal fractions:

console.log(0.1 + 0.2 === 0.3); // false! (Evaluates to 0.30000000000000004)

// Best practice comparison:
const isEqual = Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON;
console.log(isEqual); // true

5. Best Practices & Production Guidelines

  1. Default to const: Declare all variables with const by default. Only switch to let when variable reassignment is explicitly required. Never use var in modern codebases.
  2. Always use ===: Strict equality prevents unexpected coercion cascades and makes conditional logic deterministic.
  3. Avoid Direct Object Mutation: Use object spreading ({ ...obj }) or structuredClone() when creating modified copies to prevent side effects in parent callers.
  4. Leverage Optional Chaining and Nullish Coalescing: Use ?. and ?? instead of logical OR (||) when dealing with possible false or zero values.

Practice Readiness & Exam Alignment

In SkillCertify practice questions and formal certification assessments, expect questions evaluating:

  • The exact output of expressions combining strings, numbers, and booleans with + versus -.
  • Scope resolution when variables declared with var and let interact across loop blocks.
  • Distinguishing between undefined and null semantics in function parameters.

Key Takeaways

  • Primitives are stored by value and immutable; Objects are stored by reference and mutable.
  • let and const enforce block scoping and prevent access prior to declaration via the Temporal Dead Zone.
  • Loose equality (==) triggers type coercion; strict equality (===) enforces type and value identity.
  • Always check for null with val === null and NaN with Number.isNaN(val).

Next Step in Curriculum

Now that you have mastered core syntax and memory semantics, continue to Topic 2: Functions & Prototypes to explore execution contexts, closures, the four rules of this binding, and object inheritance.

Formative Practice

Test Your Understanding of Core Syntax & Types

Apply what you just learned with curated practice questions and in-depth explanations.

Practice Questions →
Advertisement