Skip to main content
JavaScript is a high-level, just-in-time compiled programming language. Technically, It is still initially interpreted for a quick startup, but it is compiled for long-term speed. Three core pillars of JavaScript are types, scope, and objects.

Types

Take a look at this example:
  1. The pre-increment operator converts the string into number first, and then returns that number before incrementing.
  2. The typeof operator returns a string indicating the data type of its operand.
  3. That type conversion happened above is called type coercion.
If you want to know more about implicit type coercion, see

JavaScript Specification

ECMAScript Language Specification
  1. In JavaScript, everything is an object is false actually. All JavaScript values, except primitives, are objects.
    1. Primitive types include undefined, string, number, boolean, object, symbol.
    2. Undeclared types include null, function, array, and bigint.
    3. functions are objects that are callable, combining function invocation with object capabilities.
      1. typeof(Function) is function even tho its not a primitive type and basically treated as object.
    4. typeof(null) is also object btw (historical bug in language?), just for backward compatibility reasons.
    5. typeof([1,2,3]) is object not array.
      1. To check if a value is an array, use Array.isArray([1,2,3]) , which returns a boolean value.
    6. bigint is also there btw, typeof(42n) is bignint.
  2. States of ‘emptiness’ in JS can be undefinedundeclared, and uninitialized (in theory only ig?)
  3. Temporal Dead Zone (TDZ) is a state where block-scoped variables are uninitialized and cannot be accessed.
  4. NaN stands for ‘Not a Number’ but actually represents invalid number.
    1. NaN === NaN is false but undefined === undefined is true
    2. Number.isNaN() does not perform type coercion, and returns true only if the value is strictly NaN.
    1. typeof(NaN) is number btw.
  5. -0 exists in JS btw, but for what? can represent a state where an object has stopped (at 0) but is still facing a specific direction (up or down).

Fundamental Objects

The JavaScript standard library includes several fundamental built-in objects used to represent core language constructs and manage data.

Type Coercion

Boxing

Boxing is the automatic process of wrapping a primitive value (e.g., a string, number, or boolean) into a temporary object of its corresponding wrapper type so that you can access methods and properties on it.

Equality

LooseEquality.js
  1. You dont really need TypeScript if you know JavaScript in depth.
  2. Using === everywhere means you not sure about the types (trust issues?)
  3. Making types known and obvious leads to better code. If types are known, == is best.

Scope

Scope basically means where to look for things. JavaScript organizes scopes with functions and blocks.
  1. Lexical scope means that the scope of variables and functions is determined by their physical location.
    • It does not depend on where the function is called at runtime.
LexicalScope.js
  1. Strict mode enforces stricter parsing and error handling, helping catch common bugs and prevent unsafe behavior.
StrictMode.js
  1. Nested scope means inner scopes can access variables from outer scopes, but outer scopes cannot access inner variables.
NestedScope.js
  1. Function declarations load before any code is executed (hoisted, can be used before definition).
  1. Function expressions load only when the interpreter reaches that line of code (only the variable is hoisted).
    1. It can be named or anonymous, but named function expressions are generally preferred for better debugging, self-documenting code and reliable function self-reference (e.g., recursion).
  1. Arrow functions should be used only when necessary for the same reasons as above.
  1. The this keyword is determined by how a function is called, rather than where it is defined, giving it behavior similar to dynamic scoping.
  1. In JavaScript, the call(), apply(), and bind() methods are all used to explicitly set the value of the this keyword inside a function.
    1. call() invokes the function immediately with a specified this value and arguments provided individually.
    1. apply() is similar to call(), but it takes arguments as an array instead of individually.
    1. bind() returns a new function with a fixed this value, but does not invoke it immediately.
  2. Function scoping ensures variables declared inside a function are local to it, preventing external access and name conflicts
    1. IIFE is a function that is defined and executed immediately, creating a new scope to avoid polluting the global namespace.
  1. Block Scoping means that variables declared with let and const are only accessible within the block they are defined in.
    • var is function-scoped and can lead to unintended consequences if used inside blocks.
  1. var is not always bad or replaced by let totally.
  1. const should be used for variables that are not meant to be reassigned, but it can be misleading when used with objects and arrays.
    • It only prevents reassignment of the variable itself, not the mutation of the object or array it references.
[!WARNING] Arrow Functions and this An arrow function is this bound (aka .bind()) to its parent function.

Closures

A closure is the combination of a function and the lexical environment within which that function was declared.
  • It allows an inner function to access variables from its outer scope, even after the outer function has finished executing.
  • Essentially, every function in JavaScript is a closure, as it automatically preserves the scope where it was created.
  • Closure can prevent garbage collection of an entire scope, variables and data might be retained in memory even if they are not directly used by the closure.
  1. Closures basically preserve a scope, not values.

Module Pattern

In JavaScript, it is a design pattern used to encapsulate code, create private and public members (variables and methods), and avoid polluting the global scope.
  • It can be achieved in the following ways:
    1. IIFE to create a private scope and return an object with public members.
  1. ES6 Modules using export and import statements to define and use modules.
  1. CommonJS Modules using module.exports and require() for server-side JavaScript (Node.js).

Hoisting

JavaScript does not actually 'hoist' code at runtime, hoisting is a conceptual model.
  1. During the compilation (parsing) phase, the engine registers declarations in memory before executing the code, which creates the appearance of hoisting.

Event Loop

Event Loop

Due to shortage of time, I’ll just explain later.

Asynchronous Programming

JavaScript is single-threaded, meaning it can only execute one operation at a time.
  1. Asynchronous programming allows handling time-consuming operations without blocking the main thread.

Callbacks

Before promises, callbacks were the primary way to handle asynchronous operations, but they often led to callback hell or pyramid of doom.

Promises

A Promise is an object representing the eventual completion or failure of an asynchronous operation.
  • It is a placeholder for a future value that will eventually be available.
  1. A promise can be in one of three states: pending, fulfilled, and rejected.
    • Once a promise is settled (fulfilled or rejected), its state cannot change.

Then, Catch, Finally

The .then() method is used to handle the resolved value of a promise, while .catch() handles rejections.
  1. .then() accepts two optional arguments: a callback for success and one for failure.
  1. .finally() runs regardless of whether the promise was fulfilled or rejected.

Promise Chaining

Each .then() returns a new promise, enabling method chaining and avoiding callback hell.
  1. If you return a value in .then(), it wraps it in a resolved promise.

Promise Combinators

JavaScript provides several static methods to work with multiple promises.
  1. Promise.all() waits for all promises to resolve, or rejects if any promise rejects.
  1. Promise.allSettled() waits for all promises to settle (fulfilled or rejected) and returns their results.
  1. Promise.race() returns the result of the first promise to settle, fulfilled or rejected.
  1. Promise.any() returns the first promise that fulfills, or rejects if all promises reject.

Async/Await

async/await is syntactic sugar over promises, making asynchronous code look synchronous.
  • An async function always returns a promise.
  • await pauses execution until the promise resolves.
  1. await can only be used inside async functions.
  2. Error handling with async/await uses try/catch.

Promises with Array Methods

Working with promises in array methods requires careful handling to avoid common pitfalls.
  1. Array.forEach() does not wait for promises and cannot be awaited.
  1. Array.map() with promises returns an array of promises that can be used with Promise.all().
  1. Sequential processing with for...of loop ensures promises execute one after another.
  1. Array.reduce() can be used for sequential promise chaining.

Common Pitfalls

  1. Forgetting to return a promise in a .then() breaks the chain.
  1. Mixing async/await with .then() unnecessarily.
  1. Not handling promise rejections.
  1. Using await in a loop unnecessarily makes operations sequential instead of parallel.
  1. Creating promises inside promises (nested promises) defeats the purpose of promise chaining.

Generators

A generator is a special function that can pause its execution and resume later, allowing you to produce a sequence of values over time.
  1. Uses the yield keyword to pause execution and return a value.
  2. It returns an iterator object with next(), return(), and throw() methods.

Generator Methods

The iterator returned by a generator has three methods for controlling execution.
  1. next() resumes execution until the next yield.
  2. return() terminates the generator and returns the provided value.
  1. throw() throws an error inside the generator that can be caught with try/catch.

Passing Values to Generators

The next() method can accept an argument that becomes the value of the yield expression.

Generators with Loops

Generators can be iterated with for...of loops, which automatically calls next() until done is true.

Async Generators

Async generators combine generators with async/await.

Classes

JavaScript classes are syntactic sugar over prototypes, introduced in ES6. They provide a cleaner syntax for creating objects and implementing inheritance.
  1. Classes are not hoisted like function declarations.
  2. Code inside classes runs in strict mode by default.
The instanceof operator tests whether an object is an instance of a class.

Constructor

The constructor method is a special method called when creating a new instance with the new keyword.

Class Methods

Constructor vs prototype

  1. Methods defined inside constructor are re-created for every new instance of the object.
  2. Methods defined on the prototype are created only once and shared among all instances, providing better performance.
  3. Prototype is like a blue-print for objects.

Static Methods

Static methods belong to the class itself, not to instances. They’re called on the class directly.

Getters and Setters

Getters and setters allow you to define methods that are accessed like properties.

Private Fields

Private fields start with # and are only accessible within the class.

Private Methods

Private methods are also prefixed with # and can only be called from within the class.

Inheritance

Classes can extend other classes using the extends keyword, inheriting properties and methods.

Super Keyword

The super keyword is used to access the parent class.
  • super() calls the parent constructor.
  • super.method() calls a parent method.

Static Inheritance

Static methods are also inherited by child classes.
[!warning] Method Overriding
  1. Child classes can override parent methods while still accessing the original via super.
[!warning] Class Expressions Classes can be defined as expressions, named or anonymous, and assigned to variables

Prototype Chain

Classes in JavaScript still use the prototype chain under the hood.

Prototypes

In JavaScript, every object has a hidden internal property called [[Prototype]] that references another object. This forms the prototype chain, which is the foundation of inheritance.
  1. The prototype chain is checked when accessing properties or methods on an object.
  2. If a property isn’t found on the object, JavaScript looks up the prototype chain.
  3. All JavaScript objects ultimately inherit from Object.prototype.

__proto__ vs prototype

__proto__ and prototype are different but related concepts.
  • prototype is a property of constructor functions.
  • __proto__ is the actual object used in the lookup chain, deprecated tho, use Object.getPrototypeOf() instead.

Dynamic Prototype Modification

You can add methods to a prototype at any time, and all instances, even existing ones, will have access to the new method.

Object.create( )

Object.create() creates a new object with a specified prototype, allowing explicit control over inheritance without using classes or constructor functions.