Types
Take a look at this example:-
The
pre-incrementoperator converts the string into number first, and then returns that number before incrementing. -
The
typeofoperator returns a string indicating the data type of its operand. -
That type conversion happened above is called
type coercion.
implicit type coercion, see
-
In JavaScript, everything is an object is false actually. All JavaScript values, except
primitives, are objects.-
Primitive types include
undefined,string,number,boolean,object,symbol. -
Undeclared types include
null,function,array, andbigint. -
functionsare objects that arecallable, combining function invocation with object capabilities.typeof(Function)isfunctioneven tho its not a primitive type and basically treated as object.
-
typeof(null)is also object btw (historical bug in language?), just for backward compatibility reasons. -
typeof([1,2,3])isobjectnotarray.- To check if a value is an array, use
Array.isArray([1,2,3]), which returns abooleanvalue.
- To check if a value is an array, use
-
bigintis also there btw,typeof(42n)isbignint.
-
Primitive types include
-
States of ‘emptiness’ in JS can be
undefined,undeclared, anduninitialized(in theory only ig?) -
Temporal Dead Zone
(TDZ)is a state where block-scoped variables are uninitialized and cannot be accessed. -
NaNstands for ‘Not a Number’ but actually represents invalid number.-
NaN === NaNisfalsebutundefined === undefinedistrue. -
Number.isNaN()does not perform type coercion, and returnstrueonly if the value is strictlyNaN.
typeof(NaN)isnumberbtw.
-
-
-0exists 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 fundamentalbuilt-in objects used to represent core language constructs and manage data.
Type Coercion
Boxing
Boxing is the automatic process of wrapping aprimitive 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
-
You dont really need
TypeScriptif you know JavaScript in depth. -
Using
===everywhere means you not sure about the types (trust issues?) -
Making
typesknown and obvious leads to better code. If types are known,==is best.
Scope
Scope basically means where to look for things. JavaScript organizes scopes withfunctions and blocks.
Lexical scopemeans 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
Strict modeenforces stricter parsing and error handling, helping catch common bugs and prevent unsafe behavior.
StrictMode.js
Nested scopemeans inner scopes can access variables from outer scopes, but outer scopes cannot access inner variables.
NestedScope.js
Function declarationsload before any code is executed (hoisted, can be used before definition).
-
Function expressionsload only when the interpreter reaches that line of code (only the variable is hoisted).- It can be
namedoranonymous, but named function expressions are generally preferred for better debugging, self-documenting code and reliable function self-reference (e.g.,recursion).
- It can be
Arrow functionsshould be used only when necessary for the same reasons as above.
- The
thiskeyword is determined by how a function is called, rather than where it is defined, giving it behavior similar todynamic scoping.
-
In JavaScript, the
call(),apply(), andbind()methods are all used to explicitly set the value of thethiskeyword inside a function.call()invokes the function immediately with a specifiedthisvalue and arguments provided individually.
apply()is similar tocall(), but it takes arguments as an array instead of individually.
bind()returns a new function with a fixedthisvalue, but does not invoke it immediately.
-
Function scopingensures variables declared inside a function are local to it, preventing external access and name conflictsIIFEis a function that is defined and executed immediately, creating a new scope to avoid polluting theglobal namespace.
Block Scopingmeans that variables declared withletandconstare only accessible within the block they are defined in.varis function-scoped and can lead to unintended consequences if used inside blocks.
varis not always bad or replaced bylettotally.
constshould 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
mutationof the object or array it references.
- It only prevents reassignment of the variable itself, not the
[!WARNING] Arrow Functions andthisAn arrow function isthisbound (aka.bind()) to its parent function.
Closures
Aclosure is the combination of a function and the lexical environment within which that function was declared.
-
It allows an
inner functionto access variables from itsouter scope, even after the outer function has finished executing. -
Essentially,
every functionin JavaScript is a closure, as it automatically preserves the scope where it was created. -
Closure can prevent
garbage collectionof an entire scope, variables and data might be retained in memory even if they are not directly used by the closure.
Closuresbasically preserve a scope, notvalues.
Module Pattern
In JavaScript, it is a design pattern used toencapsulate code, create private and public members (variables and methods), and avoid polluting the global scope.
-
It can be achieved in the following ways:
IIFEto create a private scope and return an object with public members.
ES6 Modulesusingexportandimportstatements to define and use modules.
CommonJS Modulesusingmodule.exportsandrequire()for server-side JavaScript (Node.js).
Hoisting
JavaScript does not actually'hoist' code at runtime, hoisting is a conceptual model.
- During the
compilation(parsing) phase, the engine registers declarations in memory before executing the code, which creates the appearance of hoisting.
Event Loop
Asynchronous Programming
JavaScript issingle-threaded, meaning it can only execute one operation at a time.
- 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 tocallback hell or pyramid of doom.
Promises
APromise 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.
-
A promise can be in one of three states:
pending,fulfilled, andrejected.- Once a promise is
settled(fulfilled or rejected), its state cannot change.
- Once a promise is
Then, Catch, Finally
The.then() method is used to handle the resolved value of a promise, while .catch() handles rejections.
.then()accepts two optionalarguments:a callback for success and one for failure.
.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.
- 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.Promise.all()waits for all promises to resolve, or rejects if any promise rejects.
Promise.allSettled()waits for all promises to settle (fulfilled or rejected) and returns their results.
Promise.race()returns the result of the first promise to settle, fulfilled or rejected.
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
asyncfunction always returns a promise. awaitpauses execution until the promise resolves.
-
awaitcan only be used insideasyncfunctions. -
Error handling with
async/awaitusestry/catch.
Promises with Array Methods
Working with promises inarray methods requires careful handling to avoid common pitfalls.
Array.forEach()does not wait for promises and cannot be awaited.
Array.map()with promises returns an array of promises that can be used withPromise.all().
- Sequential processing with
for...ofloop ensures promises execute one after another.
Array.reduce()can be used for sequential promise chaining.
Common Pitfalls
- Forgetting to
returna promise in a.then()breaks the chain.
- Mixing
async/awaitwith.then()unnecessarily.
- Not handling promise
rejections.
- Using
awaitin a loop unnecessarily makes operations sequential instead of parallel.
- Creating promises inside promises
(nested promises)defeats the purpose of promise chaining.
Generators
Agenerator is a special function that can pause its execution and resume later, allowing you to produce a sequence of values over time.
- Uses the
yieldkeyword to pause execution and return a value. - It returns an
iteratorobject withnext(),return(), andthrow()methods.
Generator Methods
The iterator returned by a generator has three methods for controlling execution.-
next()resumes execution until the nextyield. -
return()terminates the generator and returns the provided value.
throw()throws an error inside the generator that can be caught withtry/catch.
Passing Values to Generators
Thenext() method can accept an argument that becomes the value of the yield expression.
Generators with Loops
Generators can be iterated withfor...of loops, which automatically calls next() until done is true.
Async Generators
Async generators combine generators with async/await.
Classes
JavaScriptclasses are syntactic sugar over prototypes, introduced in ES6. They provide a cleaner syntax for creating objects and implementing inheritance.
- Classes are not hoisted like function declarations.
- Code inside classes runs in
strict modeby default.
instanceof operator tests whether an object is an instance of a class.
Constructor
Theconstructor method is a special method called when creating a new instance with the new keyword.
Class Methods
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 theextends keyword, inheriting properties and methods.
Super Keyword
Thesuper 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
- Child classes can override parent methods while still accessing the original via
super.
[!warning] Class Expressions Classes can be defined as expressions,namedoranonymous, 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.
- The prototype chain is
checkedwhen accessing properties or methods on an object. - If a property isn’t found on the object, JavaScript looks up the prototype chain.
- All JavaScript objects ultimately inherit from
Object.prototype.
__proto__ vs prototype
__proto__ and prototype are different but related concepts.
prototypeis a property of constructor functions.__proto__is the actual object used in the lookup chain, deprecated tho, useObject.getPrototypeOf()instead.
Dynamic Prototype Modification
You canadd 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.