Skip to main content

Fundamentals

Primitives Vs Objects

Primitive Types

Primitive values are immutable.

const name = "John";

name.toUpperCase(); // returns "JOHN"
console.log(name); // still "John"

Primitive types:

  • string
  • number
  • bigint
  • boolean
  • symbol
  • null
  • undefined

Boxed Types

JavaScript provides object wrappers for primitive types.

Examples:

String
Number
Boolean

Avoid creating boxed values directly:

const name = new String("John"); // avoid

Instead use primitive values:

const name = "John";

JavaScript automatically creates temporary wrapper objects when needed:

"hello".toUpperCase();

// is converted to
String("hello").toUpperCase();

Internally, JavaScript temporarily treats the string like a String object to access methods.

Null Vs Undefined

Undefined

Assigned automatically by JavaScript when a value has not been provided.

let user;

console.log(user); // undefined
Null

Explicitly assigned by developers to indicate the intentional absence of a value.

let user = null;

Recommendation: Use null when you want to represent "no value". Avoid manually assigning undefined.

Equality

Strict Equality (Recommended)

Always prefer ===.

1 === 1; // true
"1" === 1; // false

It compares both type and value.

Loose Equality

Avoid == because it performs type coercion.

"1" == 1; // true
false == 0; // true
Common Exception

Checking for both null and undefined:

if (value == null) {
// value is null or undefined
}

Equivalent to:

if (value === null || value === undefined) {
}

Objects

Everything that is not a primitive is an object.

Examples:

  • Objects
  • Arrays
  • Functions
  • Classes
  • Dates
  • Maps
  • Sets
  • Regular expressions

Objects are mutable.

const user = { name: "John" };

user.name = "Jane";

Object References

Objects are compared by reference, not by value.

const a = { name: "John" };
const b = { name: "John" };

console.log(a === b); // false

Even though the contents are identical, they occupy different locations in memory.

const c = a;

console.log(a === c); // true

Comparing by Value

For simple objects:

JSON.stringify(a) === JSON.stringify(b);

This works only for simple cases and is not recommended for large or complex objects.

Object.freeze()

Object.freeze() prevents modifications to an object.

const user = {
name: "John",
};

Object.freeze(user);

user.name = "Jane"; // ignored

However, freezing is shallow:

const user = {
profile: {
age: 30,
},
};

Object.freeze(user);

user.profile.age = 31; // still works

Nested objects remain mutable unless they are also frozen.

Copying Objects

Shallow Copy

Copies only the top level.

const original = {
name: "John",
address: {
city: "London",
},
};

const copy = { ...original };

copy.address.city = "Paris";

console.log(original.address.city); // Paris

Nested objects still share the same reference.

Deep Copy

Creates completely independent copies.

const copy = structuredClone(original);

Now changes to nested values do not affect the original object.

Type Checking

Typeof

Useful for primitive values.

typeof "hello"; // "string"
typeof 42; // "number"
typeof true; // "boolean"
typeof Symbol(); // "symbol"
typeof undefined; // "undefined"

Also works for functions:

typeof("hello"); // "string"
typeof(() => {}); // "function"

Typeof Null

typeof null; // "object"

This is a historical JavaScript bug kept for compatibility.

To check for null:

value === null;

Arrays

Array.isArray(value);

Avoid:

typeof value === "object";

because arrays are objects.

Integers

Number.isInteger(value);

NaN

To check for NaN:

Number.isNaN(value);
  • Do not use typeof to check NaN
typeof NaN; // "number"

Instanceof

Checks whether an object was created from a constructor.

[] instanceof Array; // true

{} instanceof Object; // true

new Date() instanceof Date; // true

/abc/ instanceof RegExp; // true

Most Precise Type Check

Object.prototype.toString.call(value);

Examples:

Object.prototype.toString.call([]);
// "[object Array]"

Object.prototype.toString.call(new Date());
// "[object Date]"

Object.prototype.toString.call(null);
// "[object Null]"
  • Create a helper to get exact type
function getType(value) {
return Object.prototype.toString
.call(value)
.slice(8, -1)
}

console.log(getType([])) // "Array"
console.log(getType({})) // "Object"
console.log(getType(null)) // "Null"
console.log(getType(undefined)) // "Undefined"
console.log(getType(new Date())) // "Date"
console.log(getType(/abc/)) // "RegExp"
console.log(getType(new Map())) // "Map"
console.log(getType(() => {})) // "Function"

Why Do We Need the Stack and Heap?

JavaScript stores data in memory using two main areas:

Stack

The stack is fast and stores small, fixed-size values.

Typical examples:

  • Function call information
  • Primitive values (string, number, boolean, etc.)
  • References (memory addresses) to objects
const age = 30;
const name = "John";

Heap

  • The heap stores larger, dynamic data structures.
  • Heap memory means: A region of memory used for dynamic allocation
  • Typical examples:
    • Objects
    • Arrays
    • Functions
    • Class instances
    • Dates, Maps, Sets, etc.
const user = {
name: "John",
age: 30,
};

The object itself lives in the heap, while the variable user stores a reference to it.

Stack
user: 0x8F42



Heap
[Free Memory box]
0x8F42 -> {
name: "John",
age: 30
}
[Free Memory box]

Difference Between var, let, and const

  • There are 3 types of scope:
    • Global scope
      • Variables declared outside any function or block.
      • Accessible throughout the program (unless shadowed by another variable).
    • Function scope
      • Variables declared inside a function.
      • Accessible only within that function.
    • Block scope (lexical scope)
      • Variables declared with let or const inside a block ().
      • Accessible only within that block.

let And const

  • Block-scoped ({})
  • Accessible only within the block where they are declared

const

  • Block-scoped
  • Must be initialized when declared.
  • Cannot be reassigned after initialization

var

  • Function-scoped
function example() {
if (true) {
var a = 1;
let b = 2;
const c = 3;
}

console.log(a); // 1
console.log(b); // ReferenceError
console.log(c); // ReferenceError
}

example();

const user = { name: "John" };

// ❌ Cannot reassign
// user = { name: "Jane" };

// ✅ But object contents can still be modified
user.name = "Jane";

Recommendation: Use const by default. Use let when the variable needs to be reassigned. Avoid var in modern JavaScript.

Logical Operators

&& and || do not necessarily return booleans.

They return one of their operands.

OR (||)

Returns the first truthy value.

const value1 = "hello" || ""; // "hello"
const value2 = false || true; // true

AND (&&)

Returns the first falsy value or the last truthy value.

"hello" && ""; // ""
true && false; // false

"hello" && "world"; // "world"
true && true; // true

Falsy Values

JavaScript has eight falsy values:

false
0
-0
0n
""
null
undefined
NaN

Everything else is truthy.

if("hello" && ""){
//won't reach here because "" is falsy
}

Type Conversion

Number Conversion

Number("123"); // 123

parseInt("123px"); // 123

parseFloat("12.5px"); // 12.5

Unary Plus

A short way to convert to a number:

const age = +"42";

String Conversion

String(123); // "123"

Boolean Conversion

Boolean(1); // true

Boolean(0); // false