How to buildOctober 11, 20267 min read
JavaScript from scratchStep 2 of 9

Variables and data types

In short

A variable is a name under which a program stores a value: in JavaScript you declare it with const if the value won't be reassigned and with let if it will. Every value has a type: seven primitive ones (number, string, boolean, undefined, null, bigint, symbol) plus objects, arrays included. You check the type with the typeof operator and compare values with strict === so JavaScript doesn't convert types behind your back.

FundamentalsLevel: BeginnerVariables & Data Types

After this you will be able to

  • Declare variables with const and let and explain when each is needed
  • Name JavaScript's seven primitive types and predict typeof results, including the special cases of null, arrays and NaN
  • Predict the result of implicit type conversion and choose === over ==
  • Process form data: trim spaces, convert a string to a number and check the result with Number.isNaN

Verified with

Node.js 24 LTS (24.18.0)Microsoft Edge 155

Checked against docs: October 11, 2026

Contents
  1. 01A variable is a name for a value: let and const
  2. 02Primitives: seven simple types
  3. 03Objects and arrays are reference types
  4. 04Type conversion: == vs ===
  5. 05Strings: templates and useful methods
  6. 06Common traps: NaN, 0.1 + 0.2, undefined
  7. 07Practice: predict the output

A variable is a name for a value: let and const

In the first program we wrote const bill = 12500 and then used bill instead of the number. That's a variable: a name under which the program remembers a value. MDN calls variables symbolic names for values. Think of a labeled box: the name is on the lid, the value is inside. Unlike many languages, JavaScript doesn't write a type on the box: the same let variable can hold a number first and a string later.

variables.js
const shopName = "Krem Coffee"; // never changes — const
let cupsSold = 0;               // will change — let

cupsSold = cupsSold + 3;
cupsSold += 2;                  // same as cupsSold = cupsSold + 2

console.log(shopName);          // → Krem Coffee
console.log(cupsSold);          // → 5

let discount;                   // declared but not assigned
console.log(discount);          // → undefined
The comment after // → is what the program prints. The code works the same in Node.js and in the browser console.

In programming, = doesn't mean “equals” but “assign”: evaluate what's on the right and store it under the name on the left. So cupsSold = cupsSold + 3 reads as “take the current value, add three and write it back”. += is shorthand for the same thing. A variable declared with let and no value holds undefined, meaning “nothing has been assigned yet”.

const forbids assigning a new value. Trying to do so is a runtime error:

const-error.js
const vatRate = 12;
vatRate = 16;
console.log(vatRate);
The rate must not change while the program runs, hence const.
terminal
$ node const-error.js
…\first-js\const-error.js:2
vatRate = 16;
        ^

TypeError: Assignment to constant variable.
    at …

Node.js v24.18.0
The file path is shortened and stack lines are omitted. The browser reports the same message: Uncaught TypeError: Assignment to constant variable.

const

Can be reassigned
No
Where the variable is visible
Inside the { } block where it's declared
When to use
By default, for anything that isn't reassigned

let

Can be reassigned
Yes
Where the variable is visible
Inside the { } block where it's declared
When to use
Counters, running totals, values that change

var

Can be reassigned
Yes
Where the variable is visible
Throughout the function, not the block
When to use
The old way: you'll see it in other people's code, new code doesn't need it
  • A name starts with a letter, _ or $; after that, digits are allowed too: total2 works, 2total doesn't.
  • Case matters: total and Total are two different variables.
  • You can't use the language's reserved words as names: let, const, if, return, class and others.
  • By convention, JavaScript names use camelCase: cupsSold, deliveryPrice. It's not a language rule, but almost all the code you'll meet is written that way.

Primitives: seven simple types

The ECMAScript standard defines eight data types: seven primitives and the object. A primitive is a simple, immutable value: a number, a string, a boolean. “Immutable” means you can't edit a string in place: string methods return a new string and the original stays as it was.

number

Example
12500, 0.5, -3, NaN
typeof
"number"
What it's for
All numbers, whole and fractional

string

Example
"Almaty", 'coffee', a template in backticks
typeof
"string"
What it's for
Text

boolean

Example
true, false
typeof
"boolean"
What it's for
Yes or no: is the order paid, is the item in stock

undefined

Example
undefined
typeof
"undefined"
What it's for
No value has been assigned

null

Example
null
typeof
"object"
What it's for
Intentionally empty: “no phone number”

bigint

Example
9007199254740993n
typeof
"bigint"
What it's for
Very large integers

symbol

Example
Symbol("id")
typeof
"symbol"
What it's for
Unique keys; beginners rarely need them
types.js
console.log(typeof 12500);         // → number
console.log(typeof 0.5);           // → number
console.log(typeof "Almaty");      // → string
console.log(typeof true);          // → boolean
console.log(typeof undefined);     // → undefined
console.log(typeof null);          // → object
console.log(typeof 9007199254740993n); // → bigint
console.log(typeof Symbol("id"));  // → symbol

console.log(typeof { id: 1042 });  // → object
console.log(typeof [1, 2, 3]);     // → object
console.log(Array.isArray([1, 2, 3])); // → true
console.log(typeof NaN);           // → number
typeof returns the type name as a string.

Three results are worth remembering. typeof null gives "object", a leftover from the very first implementation of JavaScript that has stayed in the language; MDN advises checking for null with value === null. An array is also "object" to typeof; Array.isArray tells it apart. And NaN, “not a number”, is formally of type number: it's a special numeric value you get when a calculation fails.

Objects and arrays are reference types

An object is a set of key–value pairs: an order with a number, an amount and a status. An array is an ordered list: the lines on a receipt, a client's phone numbers. Both are objects, and an important rule applies to them: a variable holds not the object itself but a reference to it. Assign the object to another variable and no copy appears; both variables point to the same thing.

references.js
const order = { id: 1042, total: 13750, status: "new" };
order.status = "paid";             // const doesn't stop you changing the contents
console.log(order.status);         // → paid

const sameOrder = order;           // a second reference to the same object
sameOrder.total = 0;
console.log(order.total);          // → 0

const copy = { ...order };         // a new object with the same properties
copy.total = 9990;
console.log(order.total);          // → 0
console.log(copy.total);           // → 9990

console.log([1, 2] === [1, 2]);    // → false
console.log(sameOrder === order);  // → true
A change made through sameOrder is visible through order: it's one object.

This also explains how const behaves with objects. As MDN explains, const prevents reassignment, not mutation: you can't replace the object as a whole, but you can change its properties. To get an independent copy, use the { ...order } syntax. The copy is shallow, though: nested objects inside are still shared.

Type conversion: == vs ===

JavaScript is dynamically typed: you don't declare a variable's type up front, and values are converted automatically when needed. That's convenient as long as the conversion happens where you expect it. The main trap is plus: if at least one operand is a string, + joins strings. The other arithmetic operators do the opposite and turn strings into numbers.

conversion.js
// Implicit conversion
console.log("37" + 7);             // → 377
console.log("37" - 7);             // → 30
console.log("5" * "2");            // → 10

// Explicit conversion
console.log(Number("12500"));      // → 12500
console.log(Number(" 42 "));       // → 42
console.log(Number("12 500"));     // → NaN
console.log(Number(""));           // → 0
console.log(Number(true));         // → 1
console.log(parseInt("12px", 10)); // → 12
console.log(String(42) + "1");     // → 421
console.log(Boolean(""));          // → false
console.log(Boolean("0"));         // → true
JavaScript does implicit conversion; explicit conversion is up to you.

It's safer to convert explicitly: Number(value), String(value), Boolean(value). Watch out for two traps. Number("") gives 0, not NaN, so an empty form field silently becomes zero. And Number("12 500") gives NaN: a space inside a number isn't allowed. If a string starts with a number followed by text, as in "12px", use parseInt; MDN recommends always passing the radix, 10, as its second argument.

equality.js
console.log(0 == "0");             // → true
console.log(0 === "0");            // → false
console.log("" == 0);              // → true
console.log("" === 0);             // → false
console.log(null == undefined);    // → true
console.log(null === undefined);   // → false
console.log(1 === 1.0);            // → true
console.log("Alma" === "alma");    // → false
== converts types before comparing; === doesn't.

== (loose equality)

  • Converts values to a common type before comparing
  • 0 == "0" and "" == 0 are true
  • The conversion rules are long and non-obvious
  • Bugs show up later, on rare data

=== (strict equality)

  • Different types are immediately false, no conversion
  • 0 === "0" is false
  • The result is easy to predict
  • Its “not equal” partner is !==

Strings: templates and useful methods

You can write a string in double quotes "…", single quotes '…' or backticks (usually the key to the left of 1). Backticks make a template literal: values are inserted with ${expression}, and it can span several lines. A string has a length and dozens of methods; here are the ones you'll need most often when working with form data.

strings.js
const rawName = "  aliya  ";
const city = "Almaty";

const clientName = rawName.trim();
console.log(clientName.length);              // → 5
console.log(clientName.toUpperCase());       // → ALIYA
console.log(clientName[0].toUpperCase() + clientName.slice(1)); // → Aliya
console.log(`Client: ${clientName}, ${city}`); // → Client: aliya, Almaty
console.log(`[${rawName}]`);                 // → [  aliya  ]

const phone = "+7 701 123 45 67";
console.log(phone.replaceAll(" ", ""));     // → +77011234567
console.log(phone.includes("701"));         // → true
console.log(phone.at(-1));                  // → 7
console.log(String(7).padStart(3, "0"));    // → 007
Methods don't change the original string: rawName still has its spaces after trim().

trim()

What it does
Removes spaces at both ends
Example
" aliya ".trim() → "aliya"

toUpperCase() / toLowerCase()

What it does
Changes case
Example
"aliya".toUpperCase() → "ALIYA"

slice(start, end)

What it does
Cuts out part of a string
Example
"Almaty".slice(0, 3) → "Alm"

includes(text)

What it does
Checks whether a substring is present
Example
"+7 701…".includes("701") → true

replaceAll(what, with)

What it does
Replaces every occurrence
Example
"1 2 3".replaceAll(" ", "") → "123"

at(index)

What it does
Character by position; negative counts from the end
Example
"coffee".at(-1) → "e"

padStart(length, char)

What it does
Pads on the left to a given length
Example
"7".padStart(3, "0") → "007"

Common traps: NaN, 0.1 + 0.2, undefined

pitfalls.js
// NaN isn't equal even to itself
const qty = Number("three");
console.log(qty);                  // → NaN
console.log(qty === NaN);          // → false
console.log(Number.isNaN(qty));    // → true

// Fractions are stored in binary, approximately
console.log(0.1 + 0.2);            // → 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);    // → false
console.log((0.1 + 0.2).toFixed(2)); // → 0.30

// Money is easier to handle in whole minor units: tiyn, cents
const priceTiyn = 199990;          // 1,999.90 ₸
const totalTiyn = priceTiyn * 3;
console.log(totalTiyn / 100);      // → 5999.7

// Integers above 2^53 − 1 lose precision — use BigInt for them
console.log(Number.MAX_SAFE_INTEGER);  // → 9007199254740991
console.log(9007199254740993);         // → 9007199254740992
console.log(9007199254740993n + 1n);   // → 9007199254740994n

// undefined means "no value", e.g. a property that doesn't exist
const user = { name: "Aliya" };
console.log(user.phone);           // → undefined
Every result is real output from Node.js 24 and Microsoft Edge 155.
  • NaN is the only value that isn't equal to itself, so qty === NaN is always false. Check with Number.isNaN(qty).
  • 0.1 + 0.2 isn't 0.3: numbers are stored in the binary IEEE 754 format, and 0.1 can't be written exactly in binary, just as 1/3 can't in decimal. It's a property of the format, not a JavaScript bug; other languages using this format behave the same way.
  • toFixed(2) rounds for display and returns a string, not a number: use it when printing, not in calculations.
  • Integers are stored exactly only up to Number.MAX_SAFE_INTEGER (2^53 − 1). Beyond that you need BigInt, a number ending in n; you can't mix BigInt and regular numbers in arithmetic, that throws a TypeError.
  • undefined is what you get when you read a property that doesn't exist. You set null yourself when you want to say “no value, on purpose”.

Practice: predict the output

You now know how to store values and predict what they'll turn into. The next lesson is conditions and loops: the program starts making decisions, such as rejecting a NaN quantity, and we'll look at which values JavaScript treats as “falsy”. Static types that catch errors before the code even runs come with TypeScript, which appears later in the track.

How this was verified3 · passedchecked 11 October 2026

“Run” means the code was executed in a scratch project or container and the output in the article comes from that run. “Partial” means it was checked against stubs instead of external services. “Not run” means it needs a device, an account or a key; the steps were checked against the official documentation. The verification project files and run logs are kept in the InterLAB archive.

Tell us about your task — we'll review your request and get back to you

Got a project?
We'll scope and estimate it — for free.

Tell us about the product or the problem. After reviewing the task we'll come back with an architecture, a timeline and a price for the first version.

  • Scope and risks estimated before we start
  • NDA — yours or ours
  • Work starts within a week

Please do not send passwords, access keys or source code through this form — we will agree a safe way to share access separately.

By clicking the button you agree to the processing of your personal data.