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 some JavaScript before and are comfortable reading code with functions and objects. Our goal here is not "what a string is" but how JavaScript's type system actually works: the distinction between primitives and objects, where coercion is specified versus accide
Level: AdvancedLength: In depth3 min read

Read at another depth

Values and Types

This chapter assumes you have written some JavaScript before and are comfortable reading code with functions and objects. Our goal here is not "what a string is" but how JavaScript's type system actually works: the distinction between primitives and objects, where coercion is specified versus accidental, and how equality operators resolve at the engine level.

The Seven (Plus One) Primitive Types

JavaScript's runtime has a fixed set of primitive types:

TypeExampleNotable behavior
`number``42`, `3.14`, `-0`, `NaN`, `Infinity`IEEE 754 double precision; one type for int and float
`string``"hello"`Immutable sequence of UTF-16 code units
`boolean``true`, `false`Exactly two values
`undefined``undefined`Missing value; own type and value
`null``null`Intentional absence; `typeof null === "object"` (a legacy bug)
`symbol``Symbol('id')`Unique, non-string object keys
`bigint``9007199254740993n`Arbitrary precision integers, cannot mix with `number`

The newer specification adds objects as the catch-all eighth type, with functions as callable objects. The critical structural fact: primitives are immutable and compared by value; objects are mutable and compared by identity.

Code example in javascript
let a = "hi";
a[0] = "H";           // silently ignored: strings are immutable
console.log(a);       // "hi"

const x = { v: 1 };
const y = x;
y.v = 2;
console.log(x.v);     // 2 — same reference, not a copy

Primitives are also not objects, yet you can call methods on them. The engine wraps the primitive in a temporary object (a "boxed" wrapper: String, Number, Boolean), reads the property, and discards the wrapper.

Code example in javascript
const len = "hello".length;   // 5 — works via transient String wrapper
"use strict";
x = 5;
x.foo = 1;                    // TypeError in strict mode; silently lost otherwise
console.log(x.foo);           // undefined — the wrapper was thrown away

Type Detection and Its Failure Modes

typeof is the default tool, but know its edge cases:

Code example in javascript
typeof null;             // "object" — historical bug, never fixed (breaking the web)
typeof [];               // "object" — arrays are not distinguished
typeof function(){};     // "function" — a special [[Call]] internal slot
typeof NaN;              // "number" — NaN is a number that fails self-equality
typeof undeclaredVar;    // "undefined" — safe on undeclared identifiers
typeof 10n;              // "bigint"

Reliable alternatives where typeof fails:

Code example in javascript
Array.isArray([]);               // true
Object.prototype.toString.call(null); // "[object Null]" — the robust internal check
Number.isNaN(NaN);               // true — unlike global isNaN, no coercion
Object.is(-0, 0);                // false — distinguishes signed zero

NaN deserves emphasis: NaN === NaN is false by IEEE 754 specification. Detect it with Number.isNaN(value), which does not coerce its argument — unlike the global isNaN, which converts first (isNaN("foo") is true).

Coercion: Explicit vs Implicit

Coercion is conversion between types. Explicit coercion uses String(), Number(), Boolean(). Implicit coercion happens via operators and contexts, and is the source of most type-system confusion.

The rules that matter:

  1. To number: null → 0, undefined → NaN, true → 1, "" → 0, non-numeric strings → NaN. Objects go through ToPrimitive (see below).
  2. To boolean: exactly seven values are falsy — false, 0, -0, 0n, "", null, undefined, NaN. Everything else, including [], {}, and "0", is truthy.
  3. + prefers strings: if either operand is a string (after ToPrimitive), the other is coerced to string.
  4. Arithmetic operators (-, *, /) force numbers.
Code example in javascript
1 + "2";        // "12"  (+ sees a string, concatenates)
"5" - 2;        // 3     (- forces numeric)
[] + [];        // ""    (ToPrimitive of [] is "", join of empties)
[] + {};        // "[object Object]"
0 == "";        // true  (both coerce to 0)
null == undefined; // true (spec-defined pair)
null == 0;      // false (null only equals undefined/objects — special case)
[] == false;    // true  ([] → "" → 0; false → 0)

The [] == false case is worth tracing in the spec's terms: abstract equality applies ToPrimitive to both sides, then numeric comparison. Empty array → "" → 0; false → 0; 0 == 0 → true. This is why the professional norm is: always use ===, except in the two accepted cases — x == null to check for both null and undefined at once.

ToPrimitive and ValueOf/ToString

When an object is coerced, the engine calls its Symbol.toPrimitive hint method if present; otherwise valueOf(), then toString(), in an order set by the hint ("number", "string", or "default"). The + operator uses the default hint, and ordinary objects define valueOf as returning themselves, so toString wins.

Code example in javascript
const price = {
  cents: 1250,
  [Symbol.toPrimitive](hint) {
    return hint === "number" ? this.cents : `${(this.cents / 100).toFixed(2)}`;
  },
};
console.log(+price);       // 1250  (number hint)
console.log(`${price}`);   // "12.50" (string hint)

Symbol primitives deliberately cannot coerce to string implicitly — Symbol() + "" throws — to prevent accidental symbol-key collisions in string contexts.

How Values Live in Memory

The diagram below shows that variables hold either the value itself (primitives) or a reference to a heap object:

Two consequences follow. First, copying primitives copies values; copying references shares one object — a common source of "mutation leak" bugs. Second, primitives never participate in the garbage-collection graph; only objects are allocated and collected (covered in the memory chapter).

Why This Matters

Nearly every later topic in this tutorial builds on these mechanics: == vs === behavior in default parameters, ToPrimitive in template literals, boxing in property access, identity in Map keys. Treat the type system as specified behavior rather than accumulated accidents — most of it is precisely defined, and the defined behavior is predictable once you know the coercion algorithm.

Exercise

Trace why {} == {} is false but "" == "" is true, in terms of the spec's comparison algorithms.

Show answer

String comparison is by value: identical strings compare equal because their code units match. Object comparison uses the SameValue / strict-equality algorithm, which compares references, not contents. Two distinct object literals are two distinct allocations, so their references differ and the comparison is false regardless of structure.

Exercise

You need a function isEmpty(x) that returns true for null, undefined, "", 0, and NaN, and false for [], {}, and "0". Why can you not express this with a single == comparison?

Show answer

The falsy set includes [] and {} under boolean coercion, but they are truthy for your rule, so !x fails. And NaN === NaN is false, so identity checks fail too. You must check explicitly: x == null || x === "" || x === 0 || Number.isNaN(x) — combining abstract equality for the null pair, strict equality for values, and Number.isNaN for NaN.

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