0% found this document useful (0 votes)
7 views10 pages

Inheritance and Prototypes in Es5

Uploaded by

224464.jee
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views10 pages

Inheritance and Prototypes in Es5

Uploaded by

224464.jee
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

CHAPTER 11: INHERITANCE AND PROTOTYPES IN ES5

In ES5, every object has a prototype. The prototype is itself an object, and it can have its own
prototype, forming a prototype chain. When a property is accessed on an object, JavaScript
first checks if the object itself has this property. If not, it looks up the prototype chain until it
either finds the property or reaches an object with a null prototype.

Inheritance in ES5 is achieved through this prototype mechanism. When you create a new
object, you can choose an object that should be its prototype, effectively inheriting its
properties. This is often done using constructor functions and the new keyword.

11.1. Introduction to Prototypal Inheritance in ES5

In ES5, prototypal inheritance is a key concept that enables objects to inherit properties and
methods from other objects. This section provides a fundamental understanding of how
prototypal inheritance works in JavaScript.

11.1.1. Understanding Prototypal Inheritance

Prototypal inheritance involves the concept of prototypes, where objects serve as prototypes
for other objects. This allows the sharing of properties and methods between objects, creating
a hierarchical relationship.

Syntax:

function Parent() {
this.parentProperty = 'I am a property in the parent';
}

Parent.prototype.parentMethod = function() {
console.log('I am a method in the parent');
};

function Child() {
// Call the parent constructor
Parent.call(this);
this.childProperty = 'I am a property in the child';
}

// Set up the prototype chain


Child.prototype = Object.create(Parent.prototype);

// Add a method specific to the child


Child.prototype.childMethod = function() {
console.log('I am a method in the child');
};

// Create instances
var parentInstance = new Parent();
var childInstance = new Child();

// Access properties and methods


console.log(parentInstance.parentProperty);
console.log(childInstance.childProperty);
childInstance.parentMethod();
childInstance.childMethod();

11.1.2. The Prototype Chain

The prototype chain is a series of links between objects, allowing for the inheritance of
properties and methods. Understanding the prototype chain is crucial for grasping how
objects access and inherit from their prototypes.

Example Code:

// Chain: childInstance -> Child.prototype -> Parent.prototype ->


Object.prototype
console.log(childInstance instanceof Child); // true
console.log(childInstance instanceof Parent); // true
console.log(childInstance instanceof Object); // true

11.1.3. Prototypes vs. Instances

This part distinguishes between prototypes, which are shared templates for objects, and
instances, which are individual objects created based on a prototype. It emphasizes the
separation of shared properties and unique instance properties.

Example Code:

// Prototypes
console.log(Parent.prototype); // { parentMethod: [Function] }
console.log(Child.prototype); // { parentMethod: [Function], childMethod:
[Function] }

// Instances
console.log(parentInstance); // Parent { parentProperty: 'I am a
property in the parent' }
console.log(childInstance); // Child { childProperty: 'I am a property
in the child' }

Understanding these concepts sets the foundation for effective use of prototypal inheritance
in JavaScript, allowing developers to create reusable and organized code structures.

11.2. Creating and Extending Prototypes

In ES5, creating and extending prototypes is a fundamental aspect of defining and structuring
objects. This section explores the process of establishing prototypes through constructors,
extending them, and dynamically modifying their properties.

11.2.1. Defining Prototypes with Constructors

Prototypes are typically defined using constructor functions. This allows for the creation of
objects with shared properties and methods, forming a blueprint for instances.
Syntax:

function Person(name, age) {

this.name = name;
this.age = age;
}

// Adding a method to the prototype


Person.prototype.introduce = function() {
console.log(`Hello, I'm ${this.name} and I'm ${this.age} years old.`);
};

// Creating instances
var person1 = new Person('Alice', 25);
var person2 = new Person('Bob', 30);

// Accessing shared method


person1.introduce(); // Hello, I'm Alice and I'm 25 years old.
person2.introduce(); // Hello, I'm Bob and I'm 30 years old.

11.2.2. Extending Prototypes

Extending prototypes allows for the addition of new properties and methods to existing
prototypes, enabling shared functionality across multiple objects.

Syntax:

function Student(name, age, studentId) {


// Call the parent constructor
Person.call(this, name, age);

// Additional property for the child


this.studentId = studentId;
}

// Set up the prototype chain


Student.prototype = Object.create(Person.prototype);

// Adding a method specific to the child


Student.prototype.study = function() {
console.log(`Student ${this.name} is studying.`);
};

// Creating instances
var student1 = new Student('Charlie', 22, 'S12345');
var student2 = new Student('Diana', 20, 'S67890');

// Accessing shared method and child-specific method


student1.introduce(); // Hello, I'm Charlie and I'm 22 years old.
student2.introduce(); // Hello, I'm Diana and I'm 20 years old.
student1.study(); // Student Charlie is studying.
student2.study(); // Student Diana is studying.
11.2.3. Modifying Prototypes Dynamically

Prototypes can be modified dynamically, allowing developers to adapt and enhance the
shared characteristics of objects during runtime.

Syntax:

// Modifying the prototype dynamically


Person.prototype.sayGoodbye = function() {
console.log(`Goodbye from ${this.name}!`);
};

// Accessing the new method in instances


person1.sayGoodbye(); // Goodbye from Alice!
person2.sayGoodbye(); // Goodbye from Bob!

Understanding the creation and extension of prototypes is essential for effective object-
oriented programming in JavaScript, providing a solid foundation for building scalable and
maintainable code.

11.3. Object.create() and Object.setPrototypeOf()

In JavaScript, Object.create() and Object.setPrototypeOf() are powerful tools for


managing prototypal inheritance. This section delves into the usage of these methods,
exploring their capabilities in establishing and modifying prototypes.

11.3.1. Using Object.create() for Prototypal Inheritance

Object.create() allows the creation of a new object with a specified prototype. This
facilitates a cleaner approach to prototypal inheritance, especially when working with object
literals.

Syntax:

// Creating a prototype object


var personPrototype = {
introduce: function() {
console.log(`Hello, I'm ${this.name} and I'm ${this.age} years old.`);
}
};

// Creating instances using Object.create()


var person1 = Object.create(personPrototype);
person1.name = 'Alice';
person1.age = 25;

var person2 = Object.create(personPrototype);


person2.name = 'Bob';
person2.age = 30;

// Accessing shared method


person1.introduce(); // Hello, I'm Alice and I'm 25 years old.
person2.introduce(); // Hello, I'm Bob and I'm 30 years old.
11.3.2. Setting the Prototype with Object.setPrototypeOf()

Object.setPrototypeOf() provides a way to dynamically set the prototype of an existing


object. This allows for flexibility in modifying the inheritance structure during runtime.

Syntax:

// Creating an object without a prototype


var person = {
name: 'Alice',
age: 25
};

// Creating a prototype object


var personPrototype = {
introduce: function() {
console.log(`Hello, I'm ${this.name} and I'm ${this.age} years old.`);
}
};

// Setting the prototype dynamically


Object.setPrototypeOf(person, personPrototype);

// Accessing shared method


person.introduce(); // Hello, I'm Alice and I'm 25 years old.

11.3.3. Pros and Cons of Different Prototypal Patterns

The choice between Object.create() and constructor functions with new depends on the
specific requirements of the application. Both approaches have their strengths and
considerations, and understanding these nuances is crucial for effective prototypal
inheritance.

Using Object.create() can lead to a more concise and readable code, while constructor
functions provide a familiar class-like syntax. Developers should weigh the pros and cons
based on the project's needs and coding style preferences.

11.4. Inheriting and Overriding Methods

Inheritance and method overriding are fundamental concepts in object-oriented programming.


In JavaScript ES5, these concepts are implemented through prototypes. This section explores
how to inherit methods from prototypes, override them in subtypes, and introduces the
limited use of the super keyword.

11.4.1. Inheriting Methods from the Prototype

Inheriting methods involves creating objects that share a common prototype. Methods
defined in the prototype are accessible to instances of the object, promoting code reusability.

Syntax:

// Prototype with a shared method


var animalPrototype = {
makeSound: function() {
console.log('Generic animal sound');
}
};

// Creating instances with shared methods


var cat = Object.create(animalPrototype);
cat.makeSound(); // Generic animal sound

var dog = Object.create(animalPrototype);


dog.makeSound(); // Generic animal sound

11.4.2. Overriding Methods in Subtypes

Subtypes can override methods inherited from their prototypes, providing a way to tailor
behavior to specific instances.

Syntax:

// Subtype inheriting from animalPrototype


var cat = Object.create(animalPrototype);

// Overriding the makeSound method


cat.makeSound = function() {
console.log('Meow');
};

// Calling the overridden method


cat.makeSound(); // Meow

11.4.3. Super Keyword in ES5

Unlike modern JavaScript (ES6+), ES5 lacks a direct super keyword for calling overridden
methods. Developers commonly use direct calls to the prototype's method.

Syntax:

// ES5 workaround for "super"


var cat = Object.create(animalPrototype);

cat.makeSound = function() {
// Calling the overridden method from the prototype
animalPrototype.makeSound.call(this); // Generic animal sound
console.log('Meow');
};

cat.makeSound(); // Generic animal sound followed by Meow

In ES5, method overriding is achieved by redefining a method in a subtype, and there's a


workaround for invoking the overridden method when needed. While this lacks the elegance
of the super keyword in ES6+, it remains a common practice in legacy codebases.

11.5. Implementing Multiple Inheritance


Multiple inheritance, the ability of an object to inherit from more than one parent, is not
directly supported in JavaScript. This section delves into the challenges associated with
multiple inheritance and explores a technique to mimic it in ES5.

11.5.1. Challenges of Multiple Inheritance

Multiple inheritance can lead to the "diamond problem," where a class inherits from two
classes that have a common ancestor. This creates ambiguity about which ancestor's method
to call.

11.5.2. Mimicking Multiple Inheritance in ES5

While JavaScript doesn't support multiple inheritance, developers can simulate it by


borrowing methods from other prototypes using a technique known as mixin.

Syntax:

// Parent prototypes
var Bird = {
fly: function() {
console.log('Flying');
}
};

var Mammal = {
walk: function() {
console.log('Walking');
}
};

// Child prototype with mixin


var Bat = Object.create(Bird);
for (var prop in Mammal) {
if (Mammal.hasOwnProperty(prop)) {
Bat[prop] = Mammal[prop];
}
}

// Creating an instance
var batman = Object.create(Bat);
batman.fly(); // Flying
batman.walk(); // Walking

In this example, the Bat prototype borrows methods from both Bird and Mammal. While this
provides a form of multiple inheritance, it's important to manage potential conflicts and be
aware of the limitations compared to languages explicitly designed for multiple inheritance.

11.6. Object Prototypes and Built-in Objects

Understanding object prototypes is crucial for working with built-in objects in JavaScript.
This section explores the prototypes of native objects like Array and Object, extending built-
in object prototypes, and associated risks.
11.6.1. Prototypes of Native Objects (Array, Object, etc.)

JavaScript's built-in objects, such as Array and Object, have prototypes that define their
methods and properties. Understanding these prototypes is key to leveraging the full
capabilities of these objects.

Syntax:

// Accessing Array prototype


var arrayPrototype = Array.prototype;

// Accessing Object prototype


var objectPrototype = Object.prototype;

11.6.2. Extending Built-in Object Prototypes

While it's possible to extend the prototypes of built-in objects to add custom functionality, it's
generally discouraged due to potential conflicts and unexpected behavior.

Syntax:

// Extending Array prototype (Example: adding a sum method)


Array.prototype.sum = function() {
return this.reduce(function(acc, val) {
return acc + val;
}, 0);
};

// Using the custom method


var numbers = [1, 2, 3, 4, 5];
console.log(numbers.sum()); // Output: 15

11.6.3. Risks and Considerations

Extending built-in object prototypes poses risks, including the possibility of naming conflicts
with future ECMAScript updates or other libraries. It's essential to weigh the benefits against
the risks and explore alternative approaches, such as utility functions or classes.

11.7. Common Patterns for Prototypal Inheritance

This section delves into common patterns for achieving prototypal inheritance in ES5,
including constructor functions, object linking patterns, and combining constructor and
prototype patterns.

11.7.1. Constructor Functions and Prototypes

One prevalent pattern involves using constructor functions to create objects and prototypes to
define shared methods.

Syntax:

javascript
// Constructor function
function Animal(name) {
this.name = name;
}

// Adding a method using prototype


Animal.prototype.speak = function() {
console.log(this.name + ' makes a sound');
};

// Creating an object
var cat = new Animal('Whiskers');
cat.speak(); // Output: Whiskers makes a sound

11.7.2. Object Linking Patterns

Object linking patterns involve creating an object that serves as a prototype and linking other
objects to it.

Syntax:

javascript
// Prototype object
var animalPrototype = {
speak: function() {
console.log(this.name + ' makes a sound');
}
};

// Creating an object linked to the prototype


var dog = Object.create(animalPrototype);
dog.name = 'Buddy';
dog.speak(); // Output: Buddy makes a sound

11.7.3. Combining Constructor and Prototype Patterns

A combination of constructor and prototype patterns offers flexibility by allowing instance-


specific properties and shared methods.

Syntax:

javascript
// Constructor function with prototype
function Bird(name) {
this.name = name;
}

Bird.prototype.fly = function() {
console.log(this.name + ' is flying');
};

// Creating an object
var eagle = new Bird('Majestic');
eagle.fly(); // Output: Majestic is flying

Understanding these patterns provides a foundation for effective prototypal inheritance in


ES5.
11.8. Pitfalls and Best Practices in ES5 Inheritance

This section highlights common pitfalls and best practices associated with prototypal
inheritance in ES5.

11.8.1. Common Mistakes in Prototypal Inheritance

Exploring mistakes in prototypal inheritance helps developers avoid pitfalls. Common errors
include improper modification of prototypes and misunderstanding the this keyword.

11.8.2. Dealing with Property Shadowing

Property shadowing occurs when an object has a property with the same name as one in its
prototype, potentially leading to unexpected behavior. Strategies to handle property
shadowing are discussed.

11.8.3. Best Practices for Efficient Inheritance

Efficient inheritance is crucial for optimized code. Best practices cover topics such as
minimizing prototype chain lookups and organizing code for readability and maintainability.

Understanding these pitfalls and adopting best practices ensures robust and efficient
prototypal inheritance in ES5.

11.9. Transition to ES6 Classes

11.9.1. Limitations of ES5 Prototypal Inheritance

ES5 prototypal inheritance has some limitations12. For instance, it’s not very good for storing
state. If you try to store state as objects or arrays, mutating any member of the object or array
will mutate the member for every instance that shares the prototype2. Also, you can’t mimic
Java’s “private” member variables by encapsulating a variable within a closure, but still have
it accessible to methods subsequently added to the prototype1.

11.9.2. Introduction to ES6 Classes

ES6 introduced classes to JavaScript, making it easier to create classes compared to ES534. A
class in ES6 can be created using the class keyword. A class definition can only include
constructors and functions3. The class contains the Constructors and Functions. The
Constructors take responsibility for allocating memory for the objects of the class. The function
takes responsibility for the action of the objects4. ES6 classes also support inheritance4.

11.9.3. Migrating from ES5 to ES6 Patterns

Migrating from ES5 to ES6 involves gradually moving code from ES5 to ES65. You can start
by porting the simplest function from your ES5 code to an ES6 module. Then, use a tool like
Rollup to bundle this folder into a UMD library. Compose the resulting UMD namespace with
your existing code’s namespace and delete the original function5. This process is repeated until
all code has been migrated5.

You might also like