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 the length property is directly defined on str.
  • Prototype Chain:

    • Since length is not a custom property of str (it is predefined in String.prototype), JavaScript looks at str.__proto__ (which is String.prototype for string objects).
  • Finding the Property:

    • String.prototype has the length property, which returns the number of characters in the string. JavaScript returns the value of length (12 for 'gokuthecoder').
  • No Error:

    • There is no error because length is a standard property of String.prototype, and JavaScript successfully finds it in the prototype chain.
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);