Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 1

Values and Types

This chapter assumes you have written JavaScript before, know what a function call is, and have at least run scripts in Node or a browser console. We move quickly and focus on the type system's internal rules and scoping edge cases rather than syntax basics.
Level: AdvancedLength: Mid2 min read

Read at another depth

Values and Types; Variables, let, const, and Scope

Prerequisites

This chapter assumes you have written JavaScript before, know what a function call is, and have at least run scripts in Node or a browser console. We move quickly and focus on the type system's internal rules and scoping edge cases rather than syntax basics.

The Type System

JavaScript has exactly eight types. Seven are primitives: undefined, null, boolean, number, bigint, string, and symbol. The eighth is object. Everything else—arrays, functions, dates—is an object. Primitives are immutable and compared by value; objects are compared by identity.

The critical distinction is static shape versus runtime value. typeof null === "object" is a historical bug the language cannot fix. typeof returns "function" for callables, and typeof undefined is the reliable way to test for an unset value. For arrays, use Array.isArray(x), because typeof [] === "object".

Numbers are IEEE-754 doubles. All integers above Number.MAX_SAFE_INTEGER (2^53 − 1) lose precision—JSON parsing of large IDs is the classic failure. bigint exists for this but does not mix with number in arithmetic without explicit conversion.

Coercion Is a Protocol, Not Chaos

Coercion follows spec-defined algorithms. The rules worth memorizing:

ExpressionResultRule applied
`1 + "2"``"12"`Addition prefers string if either operand is a string
`"5" - 2``3`Subtraction forces numeric
`[] + {}``"[object Object]"``ToPrimitive`: array `toString`, object `valueOf` then `toString`
`null == undefined``true`Only pair `==` treats specially
`null >= 0``true`Relational operators convert `null` to `0`

The asymmetry that trips people up: null == 0 is false (special case) while null >= 0 is true (numeric conversion). Prefer === everywhere; know =='s rules anyway, because you will read code that uses it.

Code example in javascript
console.log(9007199254740993n + 1n); // 9007199254740994n — bigint stays exact
console.log(0.1 + 0.2 === 0.3);      // false — binary floating point
console.log([1,2] == "1,2");         // true — ToPrimitive then string compare

Variables, let, const, and Scope

let and const are block-scoped and live in the Temporal Dead Zone (TDZ): the binding exists from the top of the block, but reading it before the declaration line throws a ReferenceError. This is why hoisting for let/const is uninitialized hoisting, distinct from var's hoisting-to-undefined.

Code example in javascript
{
  // console.log(x); // ReferenceError: Cannot access 'x' before initialization
  const x = 1;
}

const freezes the binding, not the value: const a = []; a.push(1) is legal. For deep immutability you need Object.freeze (shallow) plus recursion.

var is function-scoped, initialized to undefined at function entry, and attaches to globalThis at top level in scripts. In modules and classes it never appears; in legacy code it causes the loop-capture bug that let fixes:

Code example in javascript
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 3, 3, 3
for (let j = 0; j < 3; j++) setTimeout(() => console.log(j)); // 0, 1, 2

Each let iteration creates a fresh binding; var has one binding shared by all closures.

A diagram showing two loops. The var loop has one function scope with a single binding i referenced by all three callbacks. The let loop has three block scopes, each with its own binding j referenced by one callback.
100%
Scope chains for var and let in a loop
Show diagram source
graph TD
  subgraph var loop
    F[function scope: var i] --> C1[callback] & C2[callback] & C3[callback]
  end
  subgraph let loop
    B1[block: let j=0] --> K1[callback]
    B2[block: let j=1] --> K2[callback]
    B3[block: let j=2] --> K3[callback]
  end

Exercise

Why does null == 0 evaluate to false while null >= 0 evaluates to true?

Show answer

The == algorithm has a special case: null is only loosely equal to undefined and itself. Relational operators have no such case; they convert null to the number 0, so 0 >= 0 is true.

Exercise

What does typeof return for x declared as let x; inside a block, if you call typeof x before the declaration?

Show answer

It does not return "undefined". The binding is in the Temporal Dead Zone, so the expression throws a ReferenceError.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Values and Types

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that