Types, values, and variables in JavaScript

Hello! I'm a dedicated software developer with a passion for coding and a belief in technology's impact on our world. My programming journey started a few years ago and has reshaped my career and mindset. I love tackling complex problems and creating efficient code. My skills cover various languages and technologies like JavaScript, Angular, ReactJS, NodeJs, and Go Lang. I stay updated on industry trends and enjoy learning new tools. Outside of coding, I cherish small routines that enhance my workday, like sipping tea, which fuels my creativity and concentration. Whether debugging or brainstorming, it helps me focus. When I'm not coding, I engage with fellow developers. I value teamwork and enjoy mentoring newcomers and sharing my knowledge to help them grow. Additionally, I explore the blend of technology and creativity through projects that incorporate art and data visualization. This keeps my perspective fresh and my passion alive. I'm always seeking new challenges, from open-source contributions to hackathons and exploring AI. Software development is more than a job for me—it's a passion that drives continuous learning and innovation.
"Last updated: September 2026"
In this article, we will take an overview of types, values, and variables in JavaScript.
A compuer program can simply be explained as a piece of code that manipulates something.
So what is something?
Let’s ask the computer to perform some task, Hey, computer print “Hello Devs” 2 times.
So, in the above statement, there are two entities,
“Hello Devs”
2
These will be the **values **used by the computer program. First is a set of characters and the second is digit/number these are called types.
Ok, what if we want these values later in our program? Let’s save values in some container and name that container as abc. This container is called a variable.
JavaScript types mainly can be divided into two categories:-
Primitive types
Object type
Primitive types include numbers, strings of text, and boolean values(true/false). The special type of values like null and undefined are primitive values, but they are not numbers, strings, or boolean. ES6 added a new special-purpose type, known as Symbol.
Any value that is not a primitive value(number, string, boolean, symbol, null, or undefined) is an Object.
An object is a collection of properties where each property has a name and value pair. The values of an object can be a primitive value or another object.
JavaScript automatically converts values from one type to another. If the program expects a string and you provided a number, it will automatically convert the number to a string.
Numbers
The number is used to represent integers. JavaScript represents numbers using a 64-bit floating-point format defined by IEEE 754 standard.
This means it can represent numbers as large as +/- 1.797693134862315710^308 and as small as +/- 510^-324.
If we use integer values larger than the range, we may lose precision in trailing digits.
If a number appears directly in a JavaScript program it is called numeric literals. I will explain Numbers in detail in an upcoming article.
BigInt
Every regular JavaScript number is a 64-bit float, which means integers stay exact only up to a limit:
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 <-- same answer, silently wrong
Notice there is no error. It just returns a wrong number. That is the dangerous part.
BigInt (ES2020) is the seventh primitive type and it holds integers of any size. You create one with an n suffix, or with BigInt():
const big = 9007199254740991n;
console.log(big + 2n); // 9007199254740993n — correct
console.log(BigInt(42)); // 42n
console.log(typeof 10n); // "bigint"
You cannot mix BigInt with Number in arithmetic:
console.log(10n + 5); // TypeError: Cannot mix BigInt and other types
console.log(10n + BigInt(5)); // 15n
Comparison is more relaxed:
console.log(10n == 10); // true — loose equality converts across types
console.log(10n === 10); // false — bigint and number are different types
And there are no decimals. Division truncates:
console.log(5n / 2n); // 2n — there is no 2.5n
Use BigInt when you genuinely have integers past the safe range: database IDs, IDs from APIs like X/Twitter, currency stored in the smallest unit, cryptography. Do not reach for it in everyday code — it is slower than Number and it cannot do fractions.
Text
To represent text in our program JavaScript provides type as a String.
A String is an immutable ordered sequence of 16-bit values. Each 16-bit value represents a Unicode character.
The length is the number of 16-bit values that are used to represent a string. JavaScript strings use zero-based indexing, the first 16-bit value is placed at 0th index and 2nd at 1st index, and so on.
You can find details about strings in javascript in upcoming articles.
Boolean values
A boolean has only two possible values: true and false. That is the whole type.
let isLoggedIn = true;
let hasPaid = false;
console.log(5 > 3); // true
console.log("a" === "b"); // false
Comparisons always produce a boolean, which is why they can go straight into an if.
But JavaScript will accept any value where it expects a boolean, and convert it. That is where the interesting part starts.
Only eight values are falsy. Every other value in the language is truthy.
false
0
-0
0n // BigInt zero
"" // empty string
null
undefined
NaN
Memorise that list and you never have to guess again.
if ("0") {
console.log("This runs"); // "0" is a non-empty string, so it is truthy
}
if (0) {
console.log("This does not run");
}
The trap I see most often is checking an array or an object directly:
const items = [];
if (items) {
console.log("An empty array is truthy"); // this runs
}
if (items.length) {
console.log("This does not run"); // 0 is falsy, which is what we actually wanted
}
An empty array is still an object, and every object is truthy. If you want to know whether the array has anything in it, check .length.
To convert a value to a boolean on purpose, use Boolean(), or the !! shortcut:
console.log(Boolean("")); // false
console.log(Boolean("hello")); // true
console.log(!!"hello"); // true
null and undefined
Both mean "no value", but they mean it in different ways.
undefined is what JavaScript gives you. A variable you declared but never assigned, a function parameter you did not pass, a function that returns nothing, an object property that does not exist — all of these are undefined.
null is what you give JavaScript. It is a value you assign deliberately to say "there is nothing here, and I know it".
let name;
console.log(name); // undefined — declared, never assigned
let selectedUser = null; // I am deliberately saying "no user is selected"
console.log(selectedUser); // null
Now the famous oddity:
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object"
typeof null returning "object" is a bug from the very first version of JavaScript in 1995. It was never fixed, because too much code on the web already depends on the wrong answer. So do not read anything into it. null is a primitive, not an object.
To check for null specifically, compare directly:
console.log(null == undefined); // true — loose equality treats them as the same
console.log(null === undefined); // false — they are different types
console.log(selectedUser === null); // true
That null == undefined behaviour is the one case where I actually use == on purpose. value == null is a short way of asking "is this null or undefined?" — nothing else in the language is loosely equal to null.
Nullish coalescing — ??
Once you have null and undefined, you need a way to supply a fallback. Most people reach for ||, and most of the time it works. Until it doesn't.
const settings = { volume: 0, theme: null };
console.log(settings.volume || 50); // 50 <-- wrong, 0 is a valid volume
console.log(settings.volume ?? 50); // 0 <-- correct
|| falls back when the left side is falsy, so 0 and "" trigger the fallback even though they are perfectly good values. ?? falls back only when the left side is null or undefined.
console.log(settings.theme ?? "dark"); // "dark"
The rule I follow: if 0, "" or false are legal values for that variable, use ??. If any falsy value should trigger the default, || is fine.
Optional chaining — ?.
Reading a property off something that turned out to be undefined throws:
const user = { profile: { name: "Ganesh" } };
console.log(user.profile.name); // "Ganesh"
console.log(user.address.city); // TypeError: Cannot read properties of undefined (reading 'city')
?. stops the chain and gives you undefined instead of crashing:
console.log(user.address?.city); // undefined
console.log(user.profile?.["name"]); // "Ganesh" — works with bracket access too
console.log(user.getName?.()); // undefined — no crash if getName does not exist
Two things worth knowing. First, ?. only guards against null and undefined — any other error still throws. Second, it pairs naturally with ??:
const city = user.address?.city ?? "Unknown";
console.log(city); // "Unknown"
Do not scatter ?. through code out of habit. If a value should always exist, let it throw — a crash you can see beats a silent undefined that surfaces three functions later.
Symbols
A symbol is a value that is guaranteed to be unique. Every call to Symbol() produces something that will never equal anything else, ever:
const id = Symbol("id");
const id2 = Symbol("id");
console.log(id === id2); // false
The string you pass in is only a label for debugging. It has nothing to do with identity.
That uniqueness is the whole point. It lets you attach a property to an object you do not own, with zero risk of clashing with a key that is already there or one someone adds later:
const cache = Symbol("cache");
const user = { name: "Ganesh", cache: "some existing value" };
user[cache] = { hits: 0 };
console.log(user.cache); // "some existing value" — untouched
console.log(user[cache]); // { hits: 0 }
console.log(Object.keys(user)); // [ 'name', 'cache' ]
console.log(JSON.stringify(user)); // {"name":"Ganesh","cache":"some existing value"}
Notice the last two lines. Symbol keys are skipped by Object.keys() and by JSON.stringify(). They are not hidden — Object.getOwnPropertySymbols() will list them — but they stay out of the way of normal iteration.
The other place you meet symbols is the well-known symbols: a set of built-in symbols the language itself looks for. Symbol.iterator is the one you will use most. Define it on an object, and for...of and spread start working on it:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next: () =>
current <= end
? { value: current++, done: false }
: { value: undefined, done: true },
};
}
}
console.log([...new Range(1, 5)]); // [ 1, 2, 3, 4, 5 ]
We will come back to this properly when we cover iterators and generators.
Symbol.for()- This method allows us to create the same symbol value twice. Passing the same string argument to Symbol.for() method returns the same symbol value. Symbol.keyFor() returns the string that we passed as an argument to Symbol.for().
let var1 = Symbol.for(“test”);
let var2 = Symbol.for(“test”);
va1 === var2 // true
Variable Declaration and Assignment
In the programming language, we use names/identifiers to represent values.
Binding name to value gives us a way to refer to that value ad use it in the programs we write.
By doing this we can say that we are assigning value to a variable.
The term variable implies that a new value can be assigned: the value associated with the variable may vary as our program runs.
If we permanently assign some value to a name, that name we refer to as constant instead of variable.
Variable and scope
The scope of a variable is the region of our program source code in which it is defined.
Variable and constant declared with let and const are blocked scope. This means the variable is only accessible inside the code block where let or const exists.
var is function-scoped, not global
This is the distinction that matters:
function demo() {
var x = 1;
if (true) {
var y = 2; // function-scoped — still visible below
let z = 3; // block-scoped — dies at the closing brace
}
console.log(y); // 2
console.log(z); // ReferenceError: z is not defined
}
demo();
var y escapes the if block because var only respects function boundaries. let z does not. A var at the top level of a script does become a global, which is probably where the "var is global" idea comes from — but inside a function it is not.
The temporal dead zone
All three declarations are hoisted. The difference is what happens if you touch them before the declaration line runs.
console.log(a); // undefined
var a = 1;
var is hoisted and initialised to undefined, so reading it early is legal and gives you a useless value.
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 1;
let and const are hoisted too, but they are not initialised. The gap between the top of the block and the declaration line is called the temporal dead zone, and touching the variable inside it throws.
Read the error message: "Cannot access 'b' before initialization", not "b is not defined". JavaScript knows b exists. It is refusing to let you read it yet — which is exactly the bug you wanted caught.
const prevents rebinding, not mutation
This confuses almost everyone once:
const user = { name: "Ganesh" };
user.name = "Jaiwal"; // fine — I am changing what is inside the object
console.log(user.name); // "Jaiwal"
user = { name: "Someone" }; // TypeError: Assignment to constant variable.
const locks the binding between the name and the value. It says nothing about the value itself. If you want the object frozen too, that is a separate call:
const config = Object.freeze({ retries: 3 });
config.retries = 5;
console.log(config.retries); // 3 — silently ignored (throws in strict mode)
My default is const everywhere, let when I actually need to reassign, and var never.
Edit E — ADD near the end of the post
A single table readers can come back to.
Paste this as a new section:
The typeof cheat sheet
| Value | typeof returns |
|---|---|
42 |
"number" |
"hi" |
"string" |
true |
"boolean" |
undefined |
"undefined" |
10n |
"bigint" |
Symbol() |
"symbol" |
null |
"object" ← the 1995 bug |
{} |
"object" |
[] |
"object" |
new Date() |
"object" |
function () {} |
"function" |
Two things this table tells you.
typeof cannot distinguish an array from a plain object. Use Array.isArray() for that:
console.log(typeof []); // "object"
console.log(Array.isArray([])); // true
And typeof is the only operator that does not throw on an undeclared variable. That makes it the safe way to check whether something exists at all:
console.log(typeof somethingUndeclared); // "undefined" — no ReferenceError
This is an overview of Javascript types and variables.
In the next article from this series, I will cover the Number data type in detail.
Hope you like it, if yes **like & share.**
Thanks for your time.
Happy coding….





