Javascript : Object, Prototype & Proto Chaining Understand with real world example
Object
Object is entity which has some property & methods . It use to store various collection of data with key value pairs .
const person = {
fname: "Hitesh",
lname: "Chaudhry",
address: {
h: 1,
s: 2
},
greet: function(){
console.log(`Hii ${this.fname} sir, How are you ?`)
}
}
console.log(`Hey there i'm ${person.fname} ${person.lname}`);
// Output: Hey there i'm Hitesh Chaudhry
console.log(person.greet());
// Output: Hii Hitesh sir, How are you ?
This person is an object with some properties, name
, and one method, greet
. As we can see in the object, when I log properties to point to the object and access their properties and methods, we can easily do so.
Basic Operation in Javascript’s Object
Access object property
You access object property with dot
operator or braces
.
let PersonDetails= {
fname: "Yuvraj",
lname: "Saw Gupta"
};
console.log(PersonDetails.fname); // Output: Yuvraj
console.log(PersonDetails["lname"]); // Output: Saw Gupta
Add new property
let obj1 = {
h: 1,
s: 2
};
PersonDetails.address = obj1;
PersonDetails.skinColor = "brown";
console.log(PersonDetails);
// Output: { fname: 'Yuvraj', lname: 'Saw Gupta', address: { h: 1, s: 2 }, skinColor : 'brown' }
Delete property
delete PersonDetails.skinColor; // delete PersonDetails["skinColor"];
console.log(PersonDetails);
// Output: { fname: 'Yuvraj', lname: 'Saw Gupta', address: { h: 1, s: 2 } };
Modify property
PersonDetails.skinColor = "white";
// Output: { fname: 'Yuvraj', lname: 'Saw Gupta', address: { h: 1, s: 2 }, skinColor : 'white' }
Add method In Object
Generally , many coder are forget how to add method in object , bcz two or more more m
PersonDetails.greetMe = function(){
console.log(`Hey ${this.PersonDetails} is here !`)
};
PersonDetails.greetMe(); // Output: 'Hey Yuvraj is here !'
this
: The this
keyword can be tricky because when you use this
in an object, it points to that object. For example, I use this
in the greetMe
method, and greetMe
is a method of the PersonDetails object, so currently this
refers to PersonDetails.
What’s Difference b/w methods and funtion ?
Function : if a function is standalone then that si called Function .
function fullName(){
console.log(`fullname : Yuvraj Saw Gupta`);
};
Method : If a function is a part of Object then that is called methods .
const obj1 = {
fname: "Daisy",
lname "Saw Gupta"
fullName: function(){
console.log(`fullname : ${this.fname} ${this.lname}`);
}
}
Loop play on Obejct
In Jascript to loop object take in use for-in
loop ,
const randomPerson = {
fname: "John",
lname: "Doe",
age: Math.floor(Math.random() * 100),
city: "New York",
isEmployed: Math.random() > 0.5,
favoriteColor: ["Red", "Blue", "Green", "Yellow"][Math.floor(Math.random() * 4)]
};
for(let key in randomPerson){
console.log(key + " : " + randomPerson[key])
};
/* Expected Output:
'fname : John'
'lname : Doe'
'age : 5'
'city : New York'
'isEmployed : false'
'favoriteColor : Blue'
*/
Checking if a Property Exists
You can check if an object has a property using the in
operator or hasOwnProperty
.
const car = {
color: "dark",
brand: "tesla"
};
console.log("color" in car); // Output: true
console.log("rate" in car); // Output: false
Merging Objects
const obj1 = {
brand: "Tesla",
price: "3M"
}
const obj2 = {
color: "black",
plateNo: "IN9V67"
}
console.log({...obj1, ...obj2});
// Output: { brand: 'Tesla', price: '3M', color: 'black', plateNo: 'IN9V67' }
console.log(Object.assign({}, obj1, obj2));
// Output: { brand: 'Tesla', price: '3M', color: 'black', plateNo: 'IN9V67' }
console.log({...obj1, ...obj2});
work like this spread obj1
& obj2
in empty object {}
.
console.log(Object.assign({}, obj1, obj2));
work like this asssign obj1
& obj2
in an empty object .
Prototype
Defination
Prototype is like a blueprint which help to reuse code without make another copy . Because it help javascript object inherit properties and method to another .
Let understand this with real life example :
Father —- >{ skin, height, eye-color}
Father.prototype = { skin, height, eye-color}
Father has some property like skin, height , and eye-color so if that has child then child must inherit all property of his father .
Child.__proto__ = Father.prototype
But some are in doubt it just feel like child copy all property to his Father , No this is totally wrong child inherit property from his Father bcz child skin and father skin may be different or child eye-color may be not same means it vary .
Note😶🌫️: Your Aunt or uncle must be say to you have gone to your father .
The same thing happens in JavaScript when you write a string and store it in a variable. When you use the dot operator with that variable, you can see some methods are available. Have you thought about why this string variable behaves like an object and how and from where these methods come?
const str = 'gokuthecoder';
console.log(str.toUpperCase()); //Output : GOKUTHECODER
use toUppercCase
method with string , because in javascript String prototype must have this toUpperCase
method .
so simply log this String.prototype
String {'', anchor: ƒ, at: ƒ, big: ƒ, blink: ƒ, …}
anchor
:
ƒ anchor()
at
:
ƒ at()
big
:
ƒ big()
blink
:
ƒ blink()
bold
:
ƒ bold()
charAt
:
ƒ charAt()
charCodeAt
:
ƒ charCodeAt()
codePointAt
:
ƒ codePointAt()
concat
:
ƒ concat()
constructor
:
ƒ String()
endsWith
:
ƒ endsWith()
fixed
:
ƒ fixed()
fontcolor
:
ƒ fontcolor()
fontsize
:
ƒ fontsize()
includes
:
ƒ includes()
indexOf
:
ƒ indexOf()
isWellFormed
:
ƒ isWellFormed()
italics
:
ƒ italics()
lastIndexOf
:
ƒ lastIndexOf()
length
:
0
link
:
ƒ link()
localeCompare
:
ƒ localeCompare()
match
:
ƒ match()
matchAll
:
ƒ matchAll()
normalize
:
ƒ normalize()
padEnd
:
ƒ padEnd()
padStart
:
ƒ padStart()
repeat
:
ƒ repeat()
replace
:
ƒ replace()
replaceAll
:
ƒ replaceAll()
search
:
ƒ search()
slice
:
ƒ slice()
small
:
ƒ small()
split
:
ƒ split()
startsWith
:
ƒ startsWith()
strike
:
ƒ strike()
sub
:
ƒ sub()
substr
:
ƒ substr()
substring
:
ƒ substring()
sup
:
ƒ sup()
toLocaleLowerCase
:
ƒ toLocaleLowerCase()
toLocaleUpperCase
:
ƒ toLocaleUpperCase()
toLowerCase
:
ƒ toLowerCase()
toString
:
ƒ toString()
toUpperCase
:
ƒ toUpperCase()
toWellFormed
:
ƒ toWellFormed()
trim
:
ƒ trim()
trimEnd
:
ƒ trimEnd()
trimLeft
:
ƒ trimStart()
trimRight
:
ƒ trimEnd()
trimStart
:
ƒ trimStart()
valueOf
:
ƒ valueOf()
Symbol(Symbol.iterator)
:
ƒ [Symbol.iterator]()
[[Prototype]]
:
Object
[[PrimitiveValue]]
:
""
Here is intresting thing happen
const str= 'gokuthecoder';
str.__proto__ = String.prototype;
This relationship proves that str
has inherited all properties of the main String class.
const str= 'gokuthecoder';
str.length; //Output: 12
But here is an issue: as we see, all String class properties are in str.__proto__.length
, so why does JS not give any error while providing the exact result? It is because of prototype chaining.
Prototype chaining
Prototype chaining in JavaScript is a mechanism that inherits methods or properties from another object. Every object has its own prototype. When trying to access any method or property, it first checks if it has its own method or property. If not, it goes to [[prototype]]
. If the method or property is still not found, it goes to [[prototype]].[[prototype]]
. This process continues until it reaches null.
const o = {
a: 1,
b: 2,
// __proto__ sets the [[Prototype]]. It's specified here
// as another object literal.
__proto__: {
b: 3,
c: 4,
},
};
// o.[[Prototype]] has properties b and c.
// o.[[Prototype]].[[Prototype]] is Object.prototype (we will explain
// what that means later).
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain, as null,
// by definition, has no [[Prototype]].
// Thus, the full prototype chain looks like:
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null
console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called Property Shadowing
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and
// there is no 'd' property by default, check its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.
str ➡️ [ 'gokuthecoder' ] ❌ (No length property directly on str)
|
v
str.__proto__ ➡️ [ String.prototype ] ✅ (length property found here)
|
v
String.prototype ➡️ length (12) ✅
Accessing
str.length
:You call
str.length
.JavaScript first looks at
str
(your string variable).
Looking at the Object:
- The object
str
is a string, so JavaScript checks if thelength
property is directly defined onstr
.
- The object
Prototype Chain:
- Since
length
is not a custom property ofstr
(it is predefined inString.prototype
), JavaScript looks atstr.__proto__
(which isString.prototype
for string objects).
- Since
Finding the Property:
String.prototype
has thelength
property, which returns the number of characters in the string. JavaScript returns the value oflength
(12 for'gokuthecoder'
).
No Error:
- There is no error because
length
is a standard property ofString.prototype
, and JavaScript successfully finds it in the prototype chain.
- There is no error because
Array.prototype.gokuthecoder = function(){
console.log('This is hacked by Gokuthecoder .')
}
const arr2 = [1, 2, 3, 4, 5];
arr2.gokuthecoder(); // Output: This is hacked by Gokuthecoder .
Here i directly changes in Array prototype due to this when we create a array then automatically gokuthecoder
addon that proto .
arr2.__proto__ = Array.prototype;
All function are actually available in prototype .
const arr = [4, 2, 3];
const arr2 = arr.map(a => a*2);
console.log(arr2); // Output: [8, 4, 6]
Who is write this method and property in prototype 🤔?
Browser , Browser or developer of that browser you currently used is write this method and property add on String , Object, Array …etc .
Technical Committee 39 (TC39) : TC39 group decide to which new method to introduce or deprecate method .
So, in meetings, it is decided which methods or properties will run or not, and then all browsers need to include this method or property. If you need to know if a property you want to use works in a specific browser, https://caniuse.com/ is an amazing website that informs you whether a method is working in browsers or only in specific browsers like Chrome or Firefox's updated versions.
That's why when you use some websites like banking website , they might prompt you to open the website in Chrome or Firefox only.
if(!Array.prototype.fill) throw new Error('Please update your browser .')
But this is problematic because you lose your user if they cannot access your website without downloading or updating their browser.
Here comes the role of a Polyfill: A Polyfill is basically a piece of code that adds modern functionality to older browsers that do not support it.
if(!Array.prototype.fill){
Array.prototype.fill = function(){
// set-up logic
}
}
So we simply try to make our own polyfills. If any browser does not have this fill method, then we add the fill method to the Array prototype, and we can easily use the fill method with any array. This code will never throw an error on any browser, even if that browser is Internet Explorer 😅.
Create Pollyfill
forEach
if(!Array.prototype.myForEach){
Array.prototype.myForEach = function(userFn){
let arr = this;
for (let i = 0; i < arr.length; i++) {
userFn(arr[i]);
}
}
}
const arr = [1,2,3,4,5];
arr.myForEach((a)=> console.log(a*9));
at
if(!Array.prototype.myAt){
Array.prototype.myAt = function(index){
if(index < 0){
index = this.length + index ;
}
return this[index];
}
}
console.log(arr.myAt(-1));
reduce
if(!Array.prototype.myReduce){
Array.prototype.myReduce = function(userFn, initialValues = null){
let accumulator = initialValues;
for (let index = 0; index < this.length; index++) {
accumulator = userFn(accumulator, this[index])
}
return accumulator;
}
}
console.log(arr.myReduce((accumulator,currentValue)=> accumulator-currentValue, 1));
shift
if(!Array.prototype.myShift){
Array.prototype.myShift = function (){
let first = this[0];
for (let i = 0; i < this.length-1; i++) {
this[i] = this[i+1]
}
this.length = this.length - 1;
return first;
}
}
const arr3 = [10, 20, 30];
console.log(arr3.myShift());
console.log(arr3);