JavaScript Prototype

Vaibhav KashyapVaibhav Kashyap
5 min read

Understanding JavaScript is sometimes nothing less than an Adventure, today I am sharing with you one of JavaScript's concept called the prototypes. I never went into this detail with the prototypes and what made me to dive deep into this was prototype pollution.

Ever heard of it ? well I too had no idea about it, until last week when I was talking to one of my friend where he mentioned how there recent project was flagged for this vulnerability and he gave me a vague explanation about it by linking it to a Injection attack that allows attacker to manipulate the default value of your objects making your application code go south.

I was confused and looking for answers and I found it while working on a recent project where I had to add some new feature in it, but before making my changes I wanted to play around with the code to understand the flow and it came to my mind, is this code also prone to prototype pollution. What I found was something interesting and I can't wait to share the finding with you.

So, Let's get started, but before we start, let's shed some light on JavaScript's inheritance model – the prototype. I have divided this article in two parts, So the first part covers the JavaScript prototype and the next will cover about the prototype pollution.

What is Prototype in JavaScript?

Prototypes in JavaScript are a mechanism by which JS objects inherits properties from one another like inheritance. Every Object in JavaScript has a built-in property which is called prototype. The prototype is itself an object, so the prototype will have its own prototype, making what's called a prototype chain.

When you try to access a property of an object, if the property can't be found in the object itself, the prototype is searched for the property. If the property still can't be found, then the prototype's prototype is searched, and so on until either the property is found, or the end of the chain is reached, in which case undefined is returned.

let's understand this with an example:

/*creating object using javascript object literal
this will make objA to inherit all the property coming
from the prototype object properties like - toString, valueOf etc*/
let objA={} 

/* Similar to how objA was created only difference is that objB will
have name and age property of its own along with inherited prototype object
properties */
let objB={
    name:"B",
    age:20
}

/* The Object.create method gives you option to create your object based on any
other object/prototype so here we are using Object.prototype that's same as the
global prototype object that objA and objB have inherited properties from*/
let objC = Object.create(Object.prototype)

/*Object.assign: Copy the values of all of the enumerable own properties from
one or more source objects to a target object and returns the target object.
So here all the properties of objB will be copied and assigned to objD
means objB and objD will have same properties */
let objD = Object.assign({},objB)
/*we are overiding the name property 
that was copied from objB and updating its value*/
objD.name="D" 

/*here we are explicitly making a empty object where 
we don't want it to inherit anything from the 
prototype object so the objE will not have access
to any of the default properties that comes with prototype object*/
let objE = Object.create(null)

console.log(objB.age) // Outputs: 20
console.log(objB.valueOf()) // Outputs:  {name: 'B', age: 20}
console.log(objD.name) // Outputs: D
console.log(objD.age) // Outputs: 20

// the prototype object can be accessed using __proto__
console.log(objA.__proto__ === objB.__proto__) // Outputs: true
console.log(objB.__proto__ === objC.__proto__) // Outputs: true
console.log(objC.__proto__ === objD.__proto__) // Outputs: true
console.log(objD.__proto__ === objA.__proto__) // Outputs: true
console.log(objE.__proto__ === objA.__proto__) // Outputs: false

So, the above console logs were pretty straight forward. Now let's see how JavaScript behaves when we do something as below.

/*creating object using javascript object literal
this will make objA to inherit all the property coming
from the prototype object properties like - toString, valueOf etc*/
let objA={} 

/* This one is interesting because
here we are not passing the Object.prototype directly
instead we are passing the objA as a prototype for objF
So objF will inherit properties from objA 
and since objA already inherits properties from the prototype object
making objF to have access to properties of prototype object 
via objA (like multilevel inheritence)*/
let objF = Object.create(objA)

console.log(objF.__proto__ === objA.__proto__); // Outputs: false
console.log(objA.__proto__ === Object.prototype); // Outputs: true
console.log(objF.__proto__.__proto__ === objA.__proto__) // outputs: true

Here's why console.log(objF.__proto__ === objA.__proto__) returns false:

  • objF.__proto__ points to prototype object of objA

  • objA.__proto__ points to Object.prototype

  • console.log(objF.__proto__.__proto__ === objA.__proto__) outputs: true

Playing with the __proto__ Object:

Since objA, objB, objC, objD share the same proto object what would happen if I add any value to the prototype like objA.__proto__.city = "hyd"

//lets see, I think you already guessed 
console.log(objA.city) // outputs: hyd
console.log(objB.city) // outputs: hyd
console.log(objC.city) // outputs: hyd
console.log(objD.city) // outputs: hyd

It's because all these objects share the same prototype object and if one object adds or updates any thing then it will reflect for all other object that share the same prototype object.

Now what about objE and objF:

console.log(objE.city) // outputs: undefined
console.log(objF.city) // outputs: hyd

Since objF inherits objA so JavaScript first checks if objF has its own city property and since it does not have city property of its own JavaScript checks if its prototype has any city property and since that is also not present its checks the prototype of objF's prototype which is pointing to prototype of objA and there it finds the city property and prints its value and this chaining is known as prototype chaining.

Where as objE is created using Object.create(null) so it does not share the same prototype object like other objects hence it will not have access to the city property until we explicity define that property for objE.

Conclusion:

Thankyou for taking out time to learn and explore along with me, I hope you must have got something useful out of it.

prototype inheritance comes with its own set of nuances and pitfalls. While it offers a seamless approach to achieving inheritance without the complexities of traditional class-based systems, However if not handled properly this simple thing can cause a problem as serious as prototype pollution and make your application do weird things.

1
Subscribe to my newsletter

Read articles from Vaibhav Kashyap directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vaibhav Kashyap
Vaibhav Kashyap

I work as a software developer for a renowned organization that has major footprints in the insurance sector. I enjoy exploring new technologies and topics that interests me and share my learnings. I code applications that leverages languages such as Java and JavaScript and use frameworks like Spring boot and React. I also have understanding of Kubernetes and docker for deployments.