Prototype in javascript

Prototype in javascript

Introduction

In JavaScript, everything is an object, and objects have properties and methods. But where do these properties and methods come from? That’s where prototypes come in. Prototypes are a fundamental concept in JavaScript that allows objects to inherit properties and methods from other objects.

What is a Prototype?

A prototype is an object that acts as a blueprint for other objects. When you create a new object, it inherits properties and methods from its prototype. Every JavaScript object has an internal link to another object called its prototype, which is used to find properties and methods.

Object Creation

You can create objects using Object.create(), which lets a new object inherit from a specified prototype.

Direct Property Access

When accessing a property, JavaScript first looks at the object's own properties. If it's not found, it then checks the prototype chain.

In this example, myCar is created using Object.create() and inherits properties and methods from the car object. We can access the brand property and the start method just like we would with any other object.

Full size

But what happens if myCar doesn’t have a property or method that we’re trying to access? In that case, JavaScript will look up the prototype chain to see if the property or method exists there.

Wrapper Classes in JavaScript: Unlocking the Power of Primitive Types

In JavaScript, primitive types like numbers, strings, and booleans are the building blocks of our code. However, these primitive types lack the flexibility and functionality of objects. That's where wrapper classes come in – they wrap around primitive types, providing a layer of abstraction and unlocking a world of possibilities.

What are Wrapper Classes?

Wrapper classes are objects that encapsulate primitive types, allowing us to access and manipulate them as if they were objects. Think of it like a gift box – the primitive type is the gift, and the wrapper class is the box that adds an extra layer of functionality.

Let's Take a Step-by-Step Look at How Wrapper Classes Work

Suppose we have a primitive string type:

let name = 'John Doe';

We can create a wrapper class around this string using the String constructor:

let wrapper = new String(name);

Now, wrapper is an object that contains the original string value. We can access the string value using the valueOf() method:

console.log(wrapper.valueOf()); // outputs "John Doe"

But That's Not All - Wrapper Classes Offer More

Wrapper classes provide a range of methods that can be used to manipulate the underlying primitive type. For example, we can use the toUpperCase() method to convert our string to uppercase:

console.log(wrapper.toUpperCase()); // outputs "JOHN DOE"

Anecdote Time: Why Wrapper Classes Matter

Imagine you're building a web application that needs to handle user input. You want to ensure that the input is in a specific format, say uppercase. With wrapper classes, you can easily achieve this using the toUpperCase() method. Without wrapper classes, you'd have to write custom code to achieve the same result – a tedious and error-prone process.

Code Sample: Using Wrapper Classes with Numbers

Let's take a look at how wrapper classes can be used with numbers:

let num = 10;
let wrapper = new Number(num);

console.log(wrapper.valueOf()); // outputs 10
console.log(wrapper.toFixed(2)); // outputs "10.00"

In this example, we create a wrapper class around the number 10. We can then use the toFixed() method to format the number as a string with two decimal places.

The Prototype Chain

Imagine you're at a library, and you ask the librarian for a book on JavaScript. The librarian checks the catalog, but the book isn't there. So, they check the catalog of the parent library, and if it's not there, they check the catalog of the parent library's parent library, and so on. This is similar to how JavaScript looks up properties and methods in the prototype chain.

In this example, Dog inherits from Animal by setting its prototype to a new object created from Animal.prototype. This allows Dog to access the eat method defined in Animal.

The __proto__ Property

Every object in JavaScript has a __proto__ property, which points to its prototype. We can use this property to access the prototype of an object.

Full size

Injecting Functionality into Prototypes

In object-oriented programming, a prototype is an object that serves as a blueprint for other objects. It defines the properties and methods that can be shared among objects. However, sometimes we want to add new functionality to an existing prototype without modifying its original code. This is where injecting functionality into prototypes comes in.

The Problem with Modifying Prototypes

Let's say we have a Person prototype with a name property and a greet method:

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

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}`);
}

If we want to add a new method, sayGoodbye, to the Person prototype, we might be tempted to modify the original code:

Person.prototype.sayGoodbye = function() {
  console.log(`Goodbye, my name is ${this.name}`);
}

However, this approach has a major drawback: it modifies the original prototype, which can cause unintended consequences in other parts of the codebase.

Injecting Functionality with Mixins

A better approach is to use mixins, which allow us to inject new functionality into a prototype without modifying its original code. A mixin is an object that defines a set of methods that can be mixed into an existing prototype.

Let's create a mixin called GreetingMixin that adds the sayGoodbye method:

const GreetingMixin = {
  sayGoodbye: function() {
    console.log(`Goodbye, my name is ${this.name}`);
  }
}

We can then inject this mixin into the Person prototype using the Object.assign method:

Object.assign(Person.prototype, GreetingMixin);

Now, the Person prototype has the sayGoodbye method without modifying its original code.

Step-by-Step Calculation

Let's calculate the benefits of using mixins:

  1. Decoupling: Mixins allow us to decouple new functionality from the original prototype code.

  2. Reusability: Mixins can be reused across multiple prototypes, reducing code duplication.

  3. Flexibility: Mixins make it easy to add or remove functionality from a prototype without modifying its original code.

Quote

"Mixins are a powerful tool for injecting new functionality into existing prototypes without causing unintended consequences." - Unknown

Anecdote

I once worked on a project where we had to add new functionality to an existing prototype without modifying its original code. We used mixins to inject the new functionality, and it saved us from introducing bugs into the codebase.

const LoggerMixin = {
  log: function(message) {
    console.log(message);
  }
}

const ValidatorMixin = {
  validate: function(data) {
    // validation logic
  }
}

Object.assign(Person.prototype, LoggerMixin, ValidatorMixin);

In this example, we're injecting two mixins, LoggerMixin and ValidatorMixin, into the Person prototype.

Conclusion

JavaScript prototypes are a key concept that enable inheritance and object behavior in the language. By learning how the prototype chain works, you can write code that is more efficient, reusable, and easy to maintain. Whether you're using built-in objects, custom constructors, or modern ES6 classes, mastering prototypes will enhance your JavaScript skills.

Keep experimenting, explore prototype-based inheritance further, and use this powerful feature to create smarter applications! 🚀