![[Media/Pasted image 20260609221102.png]]
![[Media/Pasted image 20260609221109.png]]
![[Media/Pasted image 20260609221114.png]] ![[Media/Pasted image 20260609221157.png]] ![[Media/Pasted image 20260609221203.png]] ![[Media/Pasted image 20260609221206.png]] ![[Media/Pasted image 20260609221211.png]]
Constructor functions are functions used to create multiple similar objects.
They act like a blueprint.
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const ahmed = new Person('Ahmed', 1997);
const salaria = new Person('Salaria', 1998);A constructor function is a normal function, but it is called with the new operator.
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};By convention, constructor function names start with a capital letter.
Person
Car
User
Account
[!tip] Convention
Use a capital first letter for constructor functions so people know they should be called withnew.
const ahmed = new Person('Ahmed', 1997);This creates a new object:
Person {
firstName: 'Ahmed',
birthYear: 1997
}Another object:
const salaria = new Person('Salaria', 1998);Result:
Person {
firstName: 'Salaria',
birthYear: 1998
}When this runs:
const ahmed = new Person('Ahmed', 1997);JavaScript does 4 things:
1. Creates a new empty object: {}
2. Sets this = {}
3. Links the object to the constructor's prototype
4. Returns the object automatically[!important] Important
Thenewoperator is what makesthispoint to the new object.
{}JavaScript creates a new empty object behind the scenes.
Inside the constructor:
this.firstName = firstName;
this.birthYear = birthYear;For this call:
new Person('Ahmed', 1997);It becomes:
this.firstName = 'Ahmed';
this.birthYear = 1997;So the empty object becomes:
{
firstName: 'Ahmed',
birthYear: 1997
}The new object is linked to:
Person.prototypeThis allows objects created from Person to access methods from Person.prototype.
ahmed.__proto__ === Person.prototype; // true[!info] Note
This prototype link is what enables inheritance in JavaScript.
You do not need to write:
return this;JavaScript returns the new object automatically.
const ahmed = new Person('Ahmed', 1997);Now ahmed stores the created object.
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const ahmed = new Person('Ahmed', 1997);
const salaria = new Person('Salaria', 1998);
console.log(ahmed);
console.log(salaria);Output:
Person { firstName: 'Ahmed', birthYear: 1997 }
Person { firstName: 'Salaria', birthYear: 1998 }Inside a constructor function:
this.firstName = firstName;means:
Set firstName property on the new object
Example:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};When called with:
new Person('Ahmed', 1997);this points to the new object being created.
[!success] Remember
In constructor functions,thisrefers to the new object created bynew.
A constructor function is not a special type of function.
This is just a normal function:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};It becomes a constructor only because we call it with:
new Person('Ahmed', 1997);[!important] Key Idea
The difference is not how the function is written.
The difference is how the function is called.
Wrong:
const ahmed = Person('Ahmed', 1997);Correct:
const ahmed = new Person('Ahmed', 1997);Without new, this will not point to a new object.
In strict mode:
thiswill be:
undefined
[!danger] Be Careful
Always usenewwhen calling constructor functions.
This will not work:
const Person = (firstName, birthYear) => {
this.firstName = firstName;
this.birthYear = birthYear;
};
const ahmed = new Person('Ahmed', 1997);Arrow functions do not have their own this.
[!warning] Warning
Do not use arrow functions as constructor functions.
Use a normal function expression:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};console.log(ahmed instanceof Person);Output:
true
This checks whether ahmed was created from the Person constructor.
ahmed instanceof Person; // true[!tip] Tip
Useinstanceofto check whether an object belongs to a constructor.
Avoid this:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
this.calcAge = function () {
return 2040 - this.birthYear;
};
};Why?
Every object gets its own copy of calcAge.
const a = new Person('A', 1997);
const b = new Person('B', 1998);Both objects would store separate copies of the same method.
[!warning] Warning
Creating methods inside constructors wastes memory when many objects are created.
Better idea:
Person.prototype.calcAge = function () {
return 2040 - this.birthYear;
};Now all objects can share one method.
Object literal:
const ahmed = {
firstName: 'Ahmed',
birthYear: 1997,
};Constructor function:
const ahmed = new Person('Ahmed', 1997);Use constructor functions when you want to create many similar objects.
const user1 = new Person('Ahmed', 1997);
const user2 = new Person('Salaria', 1998);
const user3 = new Person('Ali', 2000);const ConstructorName = function (param1, param2) {
this.property1 = param1;
this.property2 = param2;
};
const objectName = new ConstructorName(value1, value2);Example:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const ahmed = new Person('Ahmed', 1997);const ahmed = Person('Ahmed', 1997);Correct:
const ahmed = new Person('Ahmed', 1997);Wrong:
const Person = (firstName, birthYear) => {
this.firstName = firstName;
};Correct:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
};Not recommended:
this.calcAge = function () {
return 2040 - this.birthYear;
};Better:
Person.prototype.calcAge = function () {
return 2040 - this.birthYear;
};A constructor function is a blueprint for creating objects.
const ahmed = new Person('Ahmed', 1997);Means:
Create a new object
Set this to that object
Add properties to it
Link it to Person.prototype
Return the object
[!success] Remember
Constructor function +new= new object created from a blueprint.
Prototype is JavaScript’s mechanism for sharing properties and methods between objects.
Every object has an internal link to another object called its prototype.
object → prototype → prototype → null
This chain is called the prototype chain.
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const ahmed = new Person('Ahmed', 1997);
const salaria = new Person('Salaria', 1998);Here, Person is a constructor function.
ahmed
salaria
are objects created from Person.
console.log(Person.prototype);Output looks like:
{
constructor: Person
}
Person.prototype is an object that will be used as the prototype for all objects created using:
new Person()[!important] Important
Person.prototypeis not the prototype ofPersonitself.
It is the prototype of objects created bynew Person().
Instead of putting methods inside the constructor, we add them to the prototype.
Person.prototype.calcAge = function () {
console.log(2047 - this.birthYear);
};Now all Person objects can use this method.
ahmed.calcAge(); // 50
salaria.calcAge(); // 49If we put a method inside the constructor:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
this.calcAge = function () {
console.log(2047 - this.birthYear);
};
};Every object gets its own copy of calcAge.
That wastes memory.
Better:
Person.prototype.calcAge = function () {
console.log(2047 - this.birthYear);
};Now all objects share one method.
[!success] Benefit
Prototypes allow method sharing instead of method duplication.
Every object has a prototype link.
console.log(ahmed.__proto__);This points to:
Person.prototypeSo this is true:
console.log(ahmed.__proto__ === Person.prototype); // true[!tip] Mental Model
Person.prototypeis the shared method box.
ahmed.__proto__is the link to that box.
| Term | Meaning |
|---|---|
Person.prototype |
Prototype object used for all instances |
ahmed.__proto__ |
Link from instance to its prototype |
Object.getPrototypeOf(ahmed) |
Modern way to read prototype link |
Modern version:
console.log(Object.getPrototypeOf(ahmed));Same as:
console.log(ahmed.__proto__);[!warning] Warning
__proto__works, but the modern recommended way is:
Object.getPrototypeOf(object);When we use new:
const ahmed = new Person('Ahmed', 1997);JavaScript does 4 steps:
1. Creates a new empty object: {}
2. Sets this to that empty object
3. Links the object to Person.prototype
4. Returns the object automaticallyStep 3 creates this link:
ahmed.__proto__ === Person.prototypeWhen you run:
ahmed.calcAge();JavaScript checks:
Does ahmed have calcAge directly?No.
Then it checks:
Does ahmed.__proto__ have calcAge?Yes.
So JavaScript uses the method from Person.prototype.
ahmed
↓
Person.prototype
↓
Object.prototype
↓
null
[!important] Important
If a property is not found on the object, JavaScript searches up the prototype chain.
console.log(Person.prototype.isPrototypeOf(ahmed)); // true
console.log(Person.prototype.isPrototypeOf(salaria)); // true
console.log(Person.prototype.isPrototypeOf(Person)); // falseThis checks whether one object is in another object’s prototype chain.
Person.prototype.isPrototypeOf(Person); // falseBecause Person.prototype is not the prototype of the constructor function Person.
It is the prototype of objects created by:
new Person()[!warning] Naming Confusion
Person.prototypedoes not mean “prototype of Person”.
It means “prototype of objects created by Person”.
You can also add properties to the prototype.
Person.prototype.species = 'Homo Sapiens';Now all instances can access it:
console.log(ahmed.species); // Homo Sapiens
console.log(salaria.species); // Homo SapiensBut species is not directly inside ahmed.
It lives in:
Person.prototypeAn own property is directly inside the object.
An inherited property comes from the prototype.
console.log(ahmed.hasOwnProperty('firstName')); // true
console.log(ahmed.hasOwnProperty('species')); // falseWhy?
firstName → directly on ahmed
species → from Person.prototype
ahmed = {
firstName: 'Ahmed',
birthYear: 1997
}But through prototype, it can access:
calcAge()
speciesSo:
ahmed.calcAge();
console.log(ahmed.species);works even though those are not directly inside ahmed.
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const ahmed = new Person('Ahmed', 1997);
const salaria = new Person('Salaria', 1998);
Person.prototype.calcAge = function () {
console.log(2047 - this.birthYear);
};
ahmed.calcAge(); // 50
salaria.calcAge(); // 49
console.log(ahmed.__proto__ === Person.prototype); // true
console.log(Person.prototype.isPrototypeOf(ahmed)); // true
console.log(Person.prototype.isPrototypeOf(salaria)); // true
console.log(Person.prototype.isPrototypeOf(Person)); // false
Person.prototype.species = 'Homo Sapiens';
console.log(ahmed.species, salaria.species);
console.log(ahmed.hasOwnProperty('firstName')); // true
console.log(ahmed.hasOwnProperty('species')); // falseWrong:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
this.calcAge = function () {
console.log(2047 - this.birthYear);
};
};Better:
Person.prototype.calcAge = function () {
console.log(2047 - this.birthYear);
};Wrong idea:
Person.prototype is prototype of PersonCorrect idea:
Person.prototype is prototype of objects created by new Person()ahmed.hasOwnProperty('firstName'); // true
ahmed.hasOwnProperty('species'); // falsespecies still works:
ahmed.species; // Homo Sapiensbecause JavaScript finds it through the prototype chain.
Constructor.prototype.methodName = function () {
// shared method
};Constructor.prototype.propertyName = value;object.__proto__;Object.getPrototypeOf(object);Constructor.prototype.isPrototypeOf(object);object.hasOwnProperty(propertyName);Prototype = shared property/method storage.
When JavaScript cannot find a property directly on an object, it looks up the prototype chain.
ahmed.calcAge()JavaScript checks:
1. ahmed
2. Person.prototype
3. Object.prototype
4. null
[!success] Remember
Constructor.prototypeis the shared template.
instance.__proto__is the link to that template.
![[Media/Pasted image 20260610075848.png]]
![[Media/Pasted image 20260610075933.png]]
![[Media/Pasted image 20260610075957.png]]
Built-in objects like arrays, functions, DOM elements, strings, etc. also use prototypes.
That means they can access methods from their prototype chain.
const arr = [3, 6, 6, 5, 6, 9, 9];
arr.map();
arr.filter();
arr.push();These methods are not directly inside arr.
They come from:
Array.prototypeEvery object has an internal prototype link.
object.__proto__Modern way:
Object.getPrototypeOf(object)If JavaScript cannot find a property/method on the object itself, it searches up the prototype chain.
object
↓
object.__proto__
↓
object.__proto__.__proto__
↓
null
[!important] Important
Prototype chain = JavaScript looking for properties/methods in parent prototype objects.
Assume we already created this:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const jonas = new Person('Jonas', 1991);Now:
console.log(jonas.__proto__);This points to:
Person.prototypeSo:
console.log(jonas.__proto__ === Person.prototype); // trueconsole.log(jonas.__proto__);
console.log(jonas.__proto__.__proto__);
console.log(jonas.__proto__.__proto__.__proto__);This means:
jonas.__proto__Output:
Person.prototypeThen:
jonas.__proto__.__proto__Output:
Object.prototypeThen:
jonas.__proto__.__proto__.__proto__Output:
null
[!tip] Mental Model
The chain ends atnull.
jonas
↓
Person.prototype
↓
Object.prototype
↓
null
Almost all normal objects in JavaScript eventually inherit from:
Object.prototypeThat is why many objects can use methods like:
hasOwnProperty()
toString()
isPrototypeOf()Example:
jonas.hasOwnProperty('firstName');hasOwnProperty() is not inside jonas.
JavaScript finds it in:
Object.prototypeconsole.dir(Person.prototype.constructor);This points back to the constructor function:
Person
So:
Person.prototype.constructor === Person; // true[!info] Note
Theconstructorproperty helps identify which constructor function created the prototype.
const arr = [3, 6, 6, 5, 6, 9, 9];This array has access to many methods:
arr.map()
arr.filter()
arr.push()
arr.includes()Because its prototype is:
Array.prototype
console.log(arr.__proto__);
console.log(arr.__proto__ === Array.prototype);Output:
true
So:
arr.__proto__ === Array.prototypemeans:
arr is linked to Array.prototype
Full chain:
arr
↓
Array.prototype
↓
Object.prototype
↓
null
These are both arrays:
const arr1 = [1, 2, 3];
const arr2 = new Array(1, 2, 3);Usually prefer:
const arr = [1, 2, 3];[!tip] Tip
[]is shorter and more common thannew Array().
const arr = [1, 2, 3];
arr.map(num => num * 2);map() is not stored directly on arr.
JavaScript searches:
1. arr
2. Array.prototype
3. Object.prototype
4. nullIt finds map() on:
Array.prototype[!success] Remember
Built-in methods likemap,filter,push, andslicelive onArray.prototype.
Array.prototype.unique = function () {
return [...new Set(this)];
};Now every array can use:
const arr = [3, 6, 6, 5, 6, 9, 9];
console.log(arr.unique());Output:
[3, 6, 5, 9]
Array.prototype.unique = function () {
return [...new Set(this)];
};Inside the method:
this
points to the array that called the method.
So:
arr.unique()means:
this → arr
Then:
new Set(this)
removes duplicates.
Then:
[...new Set(this)]
converts the Set back into an array.
Adding methods to built-in prototypes is possible, but usually not recommended.
Array.prototype.unique = function () {
return [...new Set(this)];
};[!danger] Be Careful
Do not modify built-in prototypes likeArray.prototypein real projects unless you fully understand the risks.
Problems:
name conflicts
future JavaScript updates may break your method
libraries may behave unexpectedly
harder debuggingBetter:
const unique = arr => [...new Set(arr)];
console.log(unique([3, 6, 6, 5, 9]));const h1 = document.querySelector('h1');
h1 is an object too.
So it also has a prototype chain.
Example chain:
h1
↓
HTMLHeadingElement.prototype
↓
HTMLElement.prototype
↓
Element.prototype
↓
Node.prototype
↓
EventTarget.prototype
↓
Object.prototype
↓
null
That is why DOM elements can use methods like:
addEventListener()
querySelector()
remove()
They come from prototype objects higher in the chain.
Functions are objects too.
console.dir(x => x + 1);A function can use methods like:
call()
apply()
bind()Because functions inherit from:
Function.prototypeExample:
const add = (a, b) => a + b;
add.call(null, 2, 3); // 5[!important] Important
Arrays, functions, objects, and DOM elements all use prototypal inheritance.
| Code | Meaning |
|---|---|
Array.prototype |
Shared prototype object for arrays |
arr.__proto__ |
Link from array to Array.prototype |
Person.prototype |
Shared prototype object for Person instances |
jonas.__proto__ |
Link from jonas to Person.prototype |
Modern way:
Object.getPrototypeOf(arr);
Object.getPrototypeOf(jonas);[!warning] Warning
__proto__is useful for learning, but in real code prefer:
Object.getPrototypeOf(obj)const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const jonas = new Person('Jonas', 1991);
console.log(jonas.__proto__);
console.log(jonas.__proto__.__proto__);
console.log(jonas.__proto__.__proto__.__proto__);
console.dir(Person.prototype.constructor);
const arr = [3, 6, 6, 5, 6, 9, 9];
console.log(arr.__proto__);
console.log(arr.__proto__ === Array.prototype);
console.log(arr.__proto__.__proto__);
Array.prototype.unique = function () {
return [...new Set(this)];
};
console.log(arr.unique());
const h1 = document.querySelector('h1');
console.dir(x => x + 1);Object.getPrototypeOf(obj);obj.__proto__;Constructor.prototype;Array.prototype.methodName = function () {
// shared array method
};arr.__proto__ === Array.prototype;arr.__proto__.__proto__ === Object.prototype;Built-in objects also inherit methods from prototypes.
For arrays:
arr
↓
Array.prototype
↓
Object.prototype
↓
null
For functions:
function
↓
Function.prototype
↓
Object.prototype
↓
null
For objects created by constructors:
jonas
↓
Person.prototype
↓
Object.prototype
↓
null
[!success] Remember
Built-in methods do not live directly on every object.
They are shared through prototypes.
ES6 classes are a cleaner syntax for creating objects and working with prototypes.
They are mostly syntactic sugar coat over constructor functions and prototypes.
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
}class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
}This creates a class named PersonCl.
Then we can create objects using new:
const jessica = new PersonCl('Jessica Davis', 1996);
console.log(jessica);Output:
PersonCl {
_fullName: 'Jessica Davis',
birthYear: 1996
}Classes can also be written as expressions.
const PersonCl = class {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
};[!tip] Tip
Class declarations are more common and easier to read.
The constructor() method runs automatically when we create a new object.
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
}
const jonas = new PersonCl('Jonas Schmedtmann', 1991);When this runs:
new PersonCl('Jonas Schmedtmann', 1991)JavaScript calls:
constructor('Jonas Schmedtmann', 1991)[!important] Important
Each class can have only oneconstructor()method.
Methods written inside a class are added to the class prototype.
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
calcAge() {
console.log(2037 - this.birthYear);
}
greet() {
console.log(`Hey ${this.fullName}`);
}
}Usage:
const jonas = new PersonCl('Jonas Schmedtmann', 1991);
jonas.calcAge(); // 46
jonas.greet(); // Hey Jonas SchmedtmannThis:
class PersonCl {
calcAge() {
console.log(2037 - this.birthYear);
}
}Is similar to:
PersonCl.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
};So all objects created from the class share the same method.
jonas.__proto__ === PersonCl.prototype; // true[!success] Benefit
Class methods are shared through the prototype, so each object does not get its own copy.
A getter allows us to read a method like a property.
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
get age() {
return 2037 - this.birthYear;
}
}Usage:
const jonas = new PersonCl('Jonas Schmedtmann', 1991);
console.log(jonas.age); // 46Notice:
jonas.ageNot:
jonas.age()[!important] Important
Getters are accessed like properties, not called like functions.
A setter runs when we try to set a property.
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
set fullName(name) {
if (name.includes(' ')) {
this._fullName = name;
} else {
alert(`${name} is not a full name!`);
}
}
get fullName() {
return this._fullName;
}
}Usage:
const jessica = new PersonCl('Jessica Davis', 1996);
console.log(jessica.fullName); // Jessica DavisInside the constructor:
this.fullName = fullName;This calls the setter:
set fullName(name) {
this._fullName = name;
}If we wrote this inside the setter:
this.fullName = name;It would call the setter again and again forever.
So we use a different internal property:
this._fullName[!warning] Warning
When a setter has the same name as a property, store the real value using a different name like_fullName.
set fullName(name) {
if (name.includes(' ')) {
this._fullName = name;
} else {
alert(`${name} is not a full name!`);
}
}This checks whether the name contains a space.
Valid:
'Jessica Davis'Invalid:
'Jessica'Because a full name should contain at least two words.
set fullName(name) {
if (name.includes(' ')) this._fullName = name;
else alert(`${name} is not a full name!`);
}
get fullName() {
return this._fullName;
}The setter controls how the value is saved.
The getter controls how the value is read.
jessica.fullName = 'Jessica Davis'; // setter
console.log(jessica.fullName); // getterclass PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
calcAge() {
console.log(2037 - this.birthYear);
}
greet() {
console.log(`Hey ${this.fullName}`);
}
get age() {
return 2037 - this.birthYear;
}
set fullName(name) {
if (name.includes(' ')) {
this._fullName = name;
} else {
alert(`${name} is not a full name!`);
}
}
get fullName() {
return this._fullName;
}
}
const jessica = new PersonCl('Jessica Davis', 1996);
console.log(jessica);
jessica.calcAge();
jessica.greet();
console.log(jessica.age);
console.log(jessica.fullName);| Feature | Constructor Function | ES6 Class |
|---|---|---|
| Object creation | new Person() |
new PersonCl() |
| Methods | Added manually to prototype | Written inside class |
| Syntax | Older | Cleaner |
| Uses prototypes | Yes | Yes |
Constructor function:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
Person.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
};Class version:
class PersonCl {
constructor(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
}
calcAge() {
console.log(2037 - this.birthYear);
}
}[!tip] Remember
Classes are cleaner syntax, but behind the scenes they still use prototypes.
This does not work:
const jonas = new PersonCl('Jonas', 1991);
class PersonCl {
constructor(name, birthYear) {
this.name = name;
this.birthYear = birthYear;
}
}Define the class first.
Classes can be stored in variables and passed around.
const Person = class {
constructor(name) {
this.name = name;
}
};This is possible because classes are special kinds of functions.
All code inside a class automatically runs in strict mode.
[!info] Note
You do not need to write"use strict"inside a class.
Wrong:
const jonas = PersonCl('Jonas', 1991);Correct:
const jonas = new PersonCl('Jonas', 1991);[!danger] Be Careful
Classes must be called withnew.
Wrong:
jonas.age();Correct:
jonas.age;Getters are used like properties.
Wrong:
set fullName(name) {
this.fullName = name;
}This causes infinite recursion.
Correct:
set fullName(name) {
this._fullName = name;
}class ClassName {
constructor(param1, param2) {
this.property1 = param1;
this.property2 = param2;
}
methodName() {
// instance method
}
get propertyName() {
return value;
}
set propertyName(value) {
this._propertyName = value;
}
}
const objectName = new ClassName(value1, value2);A class is a blueprint for creating objects.
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
}Means:
Use this blueprint to create many Person objects
Most important idea:
class syntax → cleaner way to use constructor functions + prototypes
[!success] Remember
ES6 classes do not replace prototypes.
They are a cleaner syntax built on top of prototypes.
Static methods are methods that belong to the class itself, not to the objects created from the class.
class PersonCl {
static hey() {
console.log('Hey there 👋');
}
}You call it like this:
PersonCl.hey();Not like this:
const jessica = new PersonCl('Jessica Davis', 1996);
jessica.hey(); // Errorclass ClassName {
static methodName() {
// code
}
}Usage:
ClassName.methodName();[!important] Important
Static methods are called on the class, not on instances.
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
calcAge() {
console.log(2037 - this.birthYear);
}
static hey() {
console.log('Hey there 👋');
console.log(this);
}
}
const jessica = new PersonCl('Jessica Davis', 1996);
jessica.calcAge(); // works
PersonCl.hey(); // worksBut this does not work:
jessica.hey(); // Error| Type | Belongs To | Called On |
|---|---|---|
| Instance method | Object instances | jessica.calcAge() |
| Static method | Class itself | PersonCl.hey() |
Example:
class PersonCl {
calcAge() {
console.log(2037 - this.birthYear);
}
static hey() {
console.log('Hey there 👋');
}
}const jessica = new PersonCl('Jessica Davis', 1996);
jessica.calcAge(); // instance method
PersonCl.hey(); // static methodInstance methods are added to:
PersonCl.prototypeStatic methods are added directly to:
PersonClSo:
PersonCl.hey();works because hey exists on the class itself.
But:
jessica.hey();does not work because hey is not on PersonCl.prototype.
[!tip] Mental Model
Static method = utility function attached to the class.
static hey() {
console.log(this);
}When called like this:
PersonCl.hey();Inside hey():
this → PersonCl
So this refers to the class itself.
[!warning] Warning
In a static method,thisdoes not refer to an instance likejessica.
It refers to the class that called the method.
Use static methods for utility/helper behavior related to a class, but not dependent on one specific object.
Example:
class PersonCl {
static species() {
return 'Homo Sapiens';
}
}
console.log(PersonCl.species()); // Homo SapiensThis does not need a specific person object.
JavaScript already uses static methods.
Array.from('Jonas');Output:
['J', 'o', 'n', 'a', 's']from() is a static method on Array.
Correct:
Array.from('Jonas');Wrong:
[1, 2, 3].from(); // ErrorBecause from() belongs to Array, not to array instances.
Before class syntax, static methods could be added directly to constructor functions.
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
Person.hey = function () {
console.log('Hey there 👋');
};
Person.hey();This is similar to:
class PersonCl {
static hey() {
console.log('Hey there 👋');
}
}PersonCl.prototype.greet = function () {
console.log(`Hey ${this.fullName}`);
};This is available to objects:
jessica.greet();But:
static hey() {
console.log('Hey there 👋');
}is available only on the class:
PersonCl.hey();[!important] Important
Prototype methods are inherited by instances.
Static methods are not inherited by instances.
Wrong:
const jessica = new PersonCl('Jessica Davis', 1996);
jessica.hey();Correct:
PersonCl.hey();Because hey() is static.
class ClassName {
constructor(value) {
this.value = value;
}
instanceMethod() {
console.log(this.value);
}
static staticMethod() {
console.log('Static method');
}
}
const obj = new ClassName('Hello');
obj.instanceMethod();
ClassName.staticMethod();Static methods belong to the class, not to objects created from the class.
PersonCl.hey();means:
Call method on the class itself
jessica.calcAge();means:
Call method on an object instance
[!success] Remember
Instance method = for individual objects.
Static method = for the class itself.
Object.create() creates a new object and manually sets its prototype.
It is another way to implement prototypal inheritance in JavaScript.
const obj = Object.create(prototypeObject);const PersonProto = {
calcAge() {
console.log(2037 - this.birthYear);
},
};
const steven = Object.create(PersonProto);This means:
steven.__proto__ === PersonProtoSo steven can use methods from PersonProto.
Object.create(proto/parentobject);Example:
const childObject = Object.create(parentObject);Meaning:
Create a new object
Link its prototype to parentObject
[!important] Important
Object.create()does not call a constructor function.
It directly links one object to another object.
const PersonProto = {
calcAge() {
console.log(2037 - this.birthYear);
},
};
const steven = Object.create(PersonProto);
steven.name = 'Steven';
steven.birthYear = 2002;
steven.calcAge(); // 35Here, calcAge() is not directly inside steven.
JavaScript finds it in:
PersonProto
console.log(steven.__proto__ === PersonProto);Output:
true
This means:
steven
↓
PersonProto
↓
Object.prototype
↓
null
[!tip] Mental Model
Object.create(PersonProto)means:
create an empty object whose parent isPersonProto.
const steven = Object.create(PersonProto);
steven.name = 'Steven';
steven.birthYear = 2002;This works, but it is repetitive if we create many objects.
Better: create an init() method.
const PersonProto = {
calcAge() {
console.log(2037 - this.birthYear);
},
init(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
},
};Now we can do:
const sarah = Object.create(PersonProto);
sarah.init('Sarah', 1979);
sarah.calcAge(); // 58Object.create() does not automatically run a constructor.
So this does not exist:
constructor()Instead, we manually create an initializer method:
init(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
}[!info] Note
init()is not special JavaScript syntax.
It is just a normal method we created.
const PersonProto = {
calcAge() {
console.log(2037 - this.birthYear);
},
init(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
},
};
const steven = Object.create(PersonProto);
steven.name = 'Steven';
steven.birthYear = 2002;
steven.calcAge();
console.log(steven.__proto__ === PersonProto); // true
const sarah = Object.create(PersonProto);
sarah.init('Sarah', 1979);
sarah.calcAge();Constructor function:
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const jonas = new Person('Jonas', 1991);Object.create():
const PersonProto = {
init(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
},
};
const jonas = Object.create(PersonProto);
jonas.init('Jonas', 1991);| Feature | Constructor Function | Object.create() |
|---|---|---|
Uses new |
Yes | No |
| Uses constructor | Yes | No |
| Sets prototype | Automatically | Manually |
| Main idea | Create from function blueprint | Create from prototype object |
Class:
class PersonCl {
constructor(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
}
calcAge() {
console.log(2037 - this.birthYear);
}
}Object.create():
const PersonProto = {
calcAge() {
console.log(2037 - this.birthYear);
},
};[!success] Key Difference
Classes and constructor functions create objects from a blueprint.
Object.create()creates objects directly from another object.
It is useful for understanding JavaScript’s prototype system clearly.
const steven = Object.create(PersonProto);This directly shows:
steven inherits from PersonProto
No constructor function.
No class syntax.
Just direct prototype linking.
const steven = Object.create(PersonProto);This does not copy methods from PersonProto.
It links steven to PersonProto.
steven.calcAge()works because JavaScript searches the prototype chain.
[!warning] Warning
Object.create()creates a prototype link.
It does not clone the prototype object.
const parentObject = {
method() {
console.log('Hello');
},
};
const childObject = Object.create(parentObject);
childObject.method();With initializer:
const PersonProto = {
init(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
},
calcAge() {
console.log(2037 - this.birthYear);
},
};
const person = Object.create(PersonProto);
person.init('Sarah', 1979);
person.calcAge();Object.create() creates a new object and sets its prototype manually.
const steven = Object.create(PersonProto);Means:
Create steven
Link steven to PersonProto
Let steven use methods from PersonProto
[!success] Remember
Object.create()is the most direct way to create prototypal inheritance in JavaScript.
Inheritance means one constructor function can reuse properties and methods from another constructor function.
Example idea:
Person → parent constructor
Student → child constructor
A Student is also a Person, but with extra data like course.
We want this:
const mike = new Student('Mike', 2020, 'Computer Science');
mike.introduce();
mike.calcAge();
introduce() should come from:
Student.prototype
calcAge() should come from:
Person.prototype
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
This creates common properties for every person:
firstName
birthYear
Person.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
};
This method is shared by all Person objects.
const jonas = new Person('Jonas', 1991);
jonas.calcAge();
[!tip] Remember
Methods should usually go on the prototype, not inside the constructor.
const Student = function (firstName, birthYear, course) {
Person.call(this, firstName, birthYear);
this.course = course;
};
Here, Student creates:
firstName
birthYear
course
But firstName and birthYear are created by reusing the Person constructor.
Inside Student, this line:
Person.call(this, firstName, birthYear);
means:
Run Person constructor
but make this point to the new Student object
So this:
const mike = new Student('Mike', 2020, 'Computer Science');
creates:
{
firstName: 'Mike',
birthYear: 2020,
course: 'Computer Science'
}
[!important] Important
We usecall()because we need to manually setthis.
Wrong:
Person(firstName, birthYear);
Correct:
Person.call(this, firstName, birthYear);
To let students use Person.prototype methods:
Student.prototype = Object.create(Person.prototype);
This creates a new empty object whose prototype is Person.prototype.
So the chain becomes:
mike
↓
Student.prototype
↓
Person.prototype
↓
Object.prototype
↓
null
[!success] Key Idea
Object.create(Person.prototype)linksStudent.prototypetoPerson.prototype.
Student.prototype.introduce = function () {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
};
Now all students can use:
mike.introduce();
Output:
My name is Mike and I study Computer Science
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
Person.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
};
const Student = function (firstName, birthYear, course) {
Person.call(this, firstName, birthYear);
this.course = course;
};
Student.prototype = Object.create(Person.prototype);
Student.prototype.introduce = function () {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
};
const mike = new Student('Mike', 2020, 'Computer Science');
mike.introduce();
mike.calcAge();
Output:
My name is Mike and I study Computer Science
17
Correct order:
Student.prototype = Object.create(Person.prototype);
Student.prototype.introduce = function () {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
};
Wrong order:
Student.prototype.introduce = function () {};
Student.prototype = Object.create(Person.prototype);
Why wrong?
Because this line:
Student.prototype = Object.create(Person.prototype);
replaces the whole Student.prototype object.
So any methods added before it will be lost.
[!warning] Warning
Link prototypes first, then add child-specific methods.
Student.prototype = Person.prototype;
This is wrong because both constructors would share the exact same prototype object.
That means changing Student.prototype would also affect Person.prototype.
Correct:
Student.prototype = Object.create(Person.prototype);
This creates a new object linked to Person.prototype.
[!danger] Be Careful
UseObject.create(), not direct assignment.
When this runs:
mike.calcAge();
JavaScript searches:
1. Does mike have calcAge?
2. Does Student.prototype have calcAge?
3. Does Person.prototype have calcAge?
It finds calcAge() in:
Person.prototype
So mike can use it.
console.log(mike.__proto__);
console.log(mike.__proto__.__proto__);
This gives:
mike.__proto__ → Student.prototype
mike.__proto__.__proto__ → Person.prototype
Modern version:
Object.getPrototypeOf(mike);
Object.getPrototypeOf(Object.getPrototypeOf(mike));
[!tip] Tip
__proto__is useful for learning, butObject.getPrototypeOf()is the modern way.
console.log(mike instanceof Student);
console.log(mike instanceof Person);
console.log(mike instanceof Object);
Output:
true
true
true
Why?
Because the prototype chain is:
mike
↓
Student.prototype
↓
Person.prototype
↓
Object.prototype
So mike is considered an instance of all three.
After this line:
Student.prototype = Object.create(Person.prototype);
The constructor property becomes incorrect.
So we fix it:
Student.prototype.constructor = Student;
Then:
console.dir(Student.prototype.constructor);
points back to:
Student
[!important] Important
After manually replacing a prototype, reset theconstructorproperty.
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
Person.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
};
const Student = function (firstName, birthYear, course) {
Person.call(this, firstName, birthYear);
this.course = course;
};
Student.prototype = Object.create(Person.prototype);
Student.prototype.introduce = function () {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
};
Student.prototype.constructor = Student;
const mike = new Student('Mike', 2020, 'Computer Science');
mike.introduce();
mike.calcAge();
console.log(mike instanceof Student); // true
console.log(mike instanceof Person); // true
console.log(mike instanceof Object); // true
Wrong:
Person(firstName, birthYear);
Correct:
Person.call(this, firstName, birthYear);
Without call(), this will not point to the new Student object.
Wrong:
Student.prototype.introduce = function () {};
Student.prototype = Object.create(Person.prototype);
Correct:
Student.prototype = Object.create(Person.prototype);
Student.prototype.introduce = function () {};
After linking:
Student.prototype = Object.create(Person.prototype);
Add:
Student.prototype.constructor = Student;
const Parent = function (prop1, prop2) {
this.prop1 = prop1;
this.prop2 = prop2;
};
Parent.prototype.parentMethod = function () {
// shared parent method
};
const Child = function (prop1, prop2, childProp) {
Parent.call(this, prop1, prop2);
this.childProp = childProp;
};
Child.prototype = Object.create(Parent.prototype);
Child.prototype.childMethod = function () {
// shared child method
};
Child.prototype.constructor = Child;
Constructor inheritance has two jobs:
1. Inherit properties
2. Inherit methods
Properties are inherited using:
Parent.call(this, ...)
Methods are inherited using:
Child.prototype = Object.create(Parent.prototype);
[!success] Remember
call()connects constructor properties.
Object.create()connects prototype methods.