Prototype in Javascript

What is prototype in JavaScript?

An encapsulation of properties that an object links to. Simply put it allows you to share functions or properties with every single object of some specific type. Now every JavaScript object has a prototype. The prototype is also an object. All JavaScript objects inherit their properties and methods from their prototype.

There are two types of prototypes:

1. A function prototype is the object instance that will become the prototype for all objects created using this function as a constructor.
2. Where as an object prototype is the object instance from which the object is inherited.

Let us take an example:

function Employee(name)
{ 
  this.name = name;
}

Now the Employee.prototype is the function prototype. If I create an object of type Employee, then it has an object prototype, which is accessible as

employee.__proto__

Please note that the Employee.prototype is pointing to the same object as employee.__proto__.

var employee = new Employee(name);
Employee.prototype ===  employee.__proto__

Similarly, if I create an another employee object then also a new object is  pointing to the Employee.prototype object.

var employee1 = new Employee(name);
employee1.__proto__ === employee.__proto__  === Employee.prototype

It means that the prototype object is shared between objects of type Employee.

How the property is read by the javascript engine?

If I type employee.salary(), it will return an error that salary method does not exist. However, if I can modify the prototype object, so that the salary() function is attached with the Employee prototype then the function salary() will be available for every single object of type Employee.

Employee.prototype.salary = function () {return 0; };

Now employee.salary() will return 0. Which means that if the object does not have a property then javascript engine will check with __proto__ object.

You can check who owns the salary object with this line of code:

employee.__proto__.hasOwnProperty('salary')

It will return true because salary is assigned to Employee prototype.

How can I change prototype object?

If you assign something to the prototype variable, a new prototype object is created. However, any existing objects are still pointing to the old prototype. so far I have two variables, employee and employee1. Both of these variables are pointing to the same prototype. Which means that the employee.age and employee1.age will return the same type i.e. undefined. However, if I change the value of a prototype age of an Employee like this:

Employee.prototype = {age: 10}

Then employee.age == employee1.age are still same, pointing to the type undefined but employee.age != Employee.prototype.

When I create a new object of an Employee like this:

var employee2 = new Employee();

The employee2 is pointing to a new prototype and employee2.age will return 10.

How inheritance works?

  • Make a call to parent function by using <base function>.call(this, <parameters>).
  • Set prototype for derived class.
  • Set prototype.constructor for derived class.
'use strict'
 
 function Employee(name){
 this.name = name
 }
 
Employee.prototype.age = 5; 
Employee.prototype.calculateSalary = function() { return 1000 };

function Manager(name) {
 // if you are not going to call the base constructor then
 // you are not going to have a name.
 Employee.call(this, name) // 1 
 this.hasCar = false;
}
 
Manager.prototype = Object.create(Employee.prototype); // 2.
Manager.prototype.constructor = Manager; //3.
 
var manager = new Manager('test');

console.log(manager.calculateSalary());

 How Prototyping is done with Classes?

'use strict'
 
 class Employee {
 constructor(name) {
 this.name = name;
 } 
 
 calculateSalary() {
 return 1000;
 }
 }
 
 class Manager extends Employee { 
 
 constructor(name, hasCar) {
 super(name);
 this.hasCar = hasCar;
 } 
 }

var manager = new Manager('test', true);
 console.log(manager.calculateSalary());

 

 

 

Playground – Javascript Object Properties.

How to define properties?

There are many ways to do this:

1. Assign a Json object aka bracket notations.

var employee = {
 name : {first: 'Vinod', last: 'Kumar'},
 gender: 'M'
};

2. Use . operator

var employee.fullName = "Amit Malhtora";

3. Use [] operator

var employee["fullName"] = "Amit Malhotra";

4. Use ECMAScript 5 defineProperty with accessor (get; set;) descriptor

Object.defineProperty(employee, 'fullName',
 {
 get: function() {
 return this.name.first + ' ' + this.name.last;
 },
 set: function (value){
 var nameParts = value.split(' ');
 this.name.first = nameParts[0];
 this.name.last = nameParts[1];
 }
});
 
employee.fullName = 'Amit Malhotra'
 
console.log(employee.name.first); // OUT: Amit

5. Use ECMAScript 5 defineProperty with property descriptor

Object.defineProperty(employee, 'fullName', {
 value: 'Amit Malhotra',
 writable: true,
 enumerable: true,
 configurable: true
});

What is Property Descriptor after all?

In JavaScript, you can define a metadata about the property. The following descriptor can be defined as writable, configurable and enumerable.

You can get the property descriptor using Object.getOwnPropertyDescriptor method.

Object.getOwnPropertyDescriptor(employee.name,'first');
 
// Out: Object
 
/* {
value: Amit
writable: true
enumerable: true
configurable: true
} */

writable – allow to change.

Object.defineProperty(employee, 'age', {writable: false});

Now if I try changing property as:

employee.age = 12

Then will throw this error:

TypeError: Cannot assign to read only property 'age' of 
object '#<Object>, Please note that it will throw an 
exception only if we use 'use strict'. Otherwise it will 
silently fail without changing the value of age to 12.

enumerable – allow to enum your property like this:

for(var propertyname in employee){
 display(propertyname + ': ' + employee[propertyname])
}

It returns name: [object Object], gender: M

If you set the enumerable to false:

Object.defineProperty(employee, 'gender', 
{enumerable: false})

 

The Object.keys(employee) will not return gender property. 
Similarly, if the Object.defineProperty(employee, 'gender',
{enumerable: true}) then it will return gender. You can 
still access gender like employee.gender, but you cannot 
see in Object.keys(employee)

configurable – That you can change some property descriptor

Object.defineProperty(employee,'age', {configurable: false})

Now you cannot change the property descriptor enumerable, configurable, or delete age. However, you can change writable.