Skip to content

Latest commit

 

History

History
610 lines (405 loc) · 24.7 KB

OOPs in JS.md

File metadata and controls

610 lines (405 loc) · 24.7 KB

🚀 OOP in JavaScript - Unveiling the Magic!

Hey there, aspiring wizards of code! 🧙‍♂️✨

Welcome to the enchanting world of Object-Oriented Programming (OOP) in JavaScript! 🌟 Today, we're diving deep into the mystical realm of classes, prototypes, and the powerful 'this' keyword.

🌈 Why This Document?

You might be wondering, "Why are we delving into the magical arts of OOP in JavaScript?" Well, fear not, for there's a grand purpose behind this journey:

💡 Unravel the Magic of Classes and Prototypes

In JavaScript, understanding OOP is like unlocking a treasure trove of possibilities. It empowers you to create reusable, organized, and enchanting code structures.

✨ Harness the Power of 'this' Keyword

The 'this' keyword, often mysterious to many, holds immense power. By the end of this document, you'll wield it with confidence, using it as your trusty magical wand to cast spells on your code.

🔮 Discover the Art of Constructors, Setters, and Getters

Ever wanted to craft objects with specific properties and behaviors? Constructors, setters, and getters are your potions for brewing objects with precision and control.

Now, let's embark on this magical journey and unravel the secrets of OOP in JavaScript! 🌌✨

🌈 Chapter 1: What is Object-Oriented Programming (OOP)?

Greetings, magical apprentices! 🧙‍♀️✨

💡 Understanding the Essence

In the vast realm of coding, Object-Oriented Programming (OOP) is a paradigm that brings order and magic to the chaotic world of code. But what does it mean?

At its core, OOP revolves around the concept of objects. An object is like a magical entity that encapsulates data and behaviors. These objects interact with each other, casting spells and collaborating to create a harmonious code symphony.

✨ Key Components

1. Objects

  • Objects are the building blocks of OOP. They encapsulate data (attributes) and actions (methods), making them powerful entities in the code kingdom.

2. Classes

  • Think of classes as blueprints or molds for creating objects. They define the structure and behavior that objects belonging to them will exhibit.

3. this Keyword

  • Brace yourselves for the magic of the 'this' keyword! It's your trusty wand, allowing objects to refer to themselves and access their own properties and methods.

4. Getter and Setter Methods

  • Unveil the secrets of accessing and modifying object properties with getter and setter methods. These enchanting methods provide controlled access to the magical attributes within an object.

5. Constructor

  • The constructor is like the magical birthplace of objects. It's a special method within a class that is invoked when a new object is created. Here, you can set up the initial state and enchantments of the object.

6. Abstraction, Encapsulation, Inheritance, Polymorphism

  • While these magical concepts exist in the realm of OOP, our focus in this article will be on Objects and Classes. The intricacies of Abstraction, Encapsulation, Inheritance, and Polymorphism shall be explored in future enchanting chapters.

🔮 Why OOP Matters

In the magical world of coding, OOP serves as a guiding light. Here's why it matters:

  • Organization: OOP provides a structured approach, making code more organized and manageable.
  • Reusability: With classes and objects, you can reuse code, saving time and effort.
  • Flexibility: OOP offers flexibility through concepts like inheritance and polymorphism, adapting to changing requirements.

So, fellow enchanters, that's a glimpse into the enchanting world of OOP! Ready yourselves, for we shall delve deeper into each magical component in the chapters to come. 🚀📜

🌈 Chapter 2: Understanding the Basics of Prototype

Ahoy, aspiring wizards! 🧙‍♂️✨

💡 Embarking on the Journey of Prototypes

In the enchanting land of JavaScript, understanding prototypes is like discovering a magical scroll that unlocks the true power of objects. Let's unravel the mystery!

📜 What is a Prototype?

In JavaScript, every object is linked to a prototype object. A prototype is like a magical ancestor, passing down properties and methods to its descendants (objects). This linkage forms a chain known as the prototype chain.

✨ Key Concepts

1. prototype Property

  • Each function in JavaScript has a special property called prototype. It's like a spellbook containing methods that objects created from the function will inherit.

2. __proto__ Property

  • The __proto__ property is a direct connection to an object's prototype. It's like a magical mirror reflecting the prototype from which an object inherits.

3. Prototypal Inheritance

  • Prototypal inheritance is the magical process where an object inherits properties and methods from its prototype. It's the essence of the enchanting connection between objects and their prototypes.

🧙 Why Prototypes Matter

Understanding prototypes is akin to wielding a powerful wand in the world of JavaScript. Here's why it matters:

  • Code Efficiency: Prototypes enable objects to share methods, reducing memory consumption and promoting efficient code.

  • Dynamic Updates: Changes to the prototype reflect in all objects linked to it, providing a dynamic and centralized way to update behaviors.

  • Code Reusability: Prototypal inheritance fosters code reuse, allowing multiple objects to inherit from a common prototype.

❇️ Example for Better Understanding

// Creating a simple function (constructor)
function Wizard(name) {
  this.name = name;
}

// Adding a method to the prototype
Wizard.prototype.castSpell = function () {
  console.log(`${this.name} casts a magical spell!`);
};

// Creating wizard objects
const merlin = new Wizard("Merlin");
const gandalf = new Wizard("Gandalf");

// Wizards inheriting and using the prototype method
merlin.castSpell(); // Output: Merlin casts a magical spell!
gandalf.castSpell(); // Output: Gandalf casts a magical spell!

📖 Delving Deeper

To truly master the art of prototypes, one must explore the depths of the prototype chain, experiment with custom prototypes, and harness the full potential of prototypal inheritance.

⛓️ The Prototype Chain

The prototype chain is a mystical connection between objects, forming a lineage of inheritance. Objects inherit properties not only from their direct prototype but also from the entire chain of prototypes.

// Example of a prototype chain
function Elf(name, power) {
  this.name = name;
  this.power = power;
}

// Adding a method to Elf's prototype
Elf.prototype.castMagic = function () {
  console.log(`${this.name} casts a powerful spell!`);
};

// Creating a DarkElf constructor that inherits from Elf
function DarkElf(name, power, darkPower) {
  // Call Elf constructor to initialize shared properties
  Elf.call(this, name, power);
  this.darkPower = darkPower;
}

// Inheriting Elf's prototype to enable shared methods
DarkElf.prototype = Object.create(Elf.prototype);

// Adding a method specific to DarkElf
DarkElf.prototype.darkCast = function () {
  console.log(`${this.name} unleashes dark power!`);
};

// Creating DarkElf objects
const legolas = new Elf("Legolas", "Bow and Arrow");
const malfoy = new DarkElf("Malfoy", "Slytherin", "Dark Arts");

// Utilizing inherited methods
legolas.castMagic(); // Output: Legolas casts a powerful spell!
malfoy.castMagic(); // Output: Malfoy casts a powerful spell!
malfoy.darkCast(); // Output: Malfoy unleashes dark power!

🔮 Custom Prototypes

Masters of the arcane arts can forge their own prototypes, imbuing objects with unique powers. Let's craft a custom prototype to empower our objects with additional abilities.

// Creating a custom prototype
const magicalBeingPrototype = {
  teleport: function (destination) {
    console.log(`${this.name} teleports to ${destination} effortlessly.`);
  },
  invisibility: function () {
    console.log(`${this.name} fades into the shadows, becoming invisible.`);
  },
};

// Creating beings with magical powers
const wizard = Object.create(magicalBeingPrototype);
wizard.name = "Gandalf";

const witch = Object.create(magicalBeingPrototype);
witch.name = "Hermione";

// Utilizing custom prototype methods
wizard.teleport("Rivendell"); // Output: Gandalf teleports to Rivendell effortlessly.
witch.invisibility(); // Output: Hermione fades into the shadows, becoming invisible.

📜 Closing the Tome of Prototypes

As we close the tome on prototypes, let's reflect on the magic woven into the fabric of JavaScript. Prototypes, the silent architects of objects, and the intricate dance of the prototype chain have unfolded their mysteries.

🌟 Lessons Learned

  • Prototypes: Blueprints guiding object creation.
  • Prototype Chain: A cosmic dance of inheritance.

🔮 Beyond Prototypes

The journey continues to classes, constructors, and the dance of this. The adventure unfolds, beckoning us to explore further.

🏰 Unveiling the Castle of Classes

In the magical kingdom of JavaScript, classes stand tall as majestic castles where objects are born. They provide a blueprint for creating objects with shared properties and methods. Behold the wonders of this castle!

// Creating a magical class
class SpellCaster {
  // Constructor to enchant new objects
  constructor(name, power) {
    this.name = name;
    this.power = power;
  }

  // Magical method within the class
  castSpell() {
    console.log(`${this.name} casts a mesmerizing spell!`);
  }
}

// Creating enchanted beings from the SpellCaster class
const sorcerer = new SpellCaster("Merlin", "Arcane Magic");
const enchantress = new SpellCaster("Morgana", "Dark Sorcery");

// Utilizing the magical powers of the class
sorcerer.castSpell(); // Output: Merlin casts a mesmerizing spell!
enchantress.castSpell(); // Output: Morgana casts a mesmerizing spell!

👷 Constructors at Work

In the magical kingdom of JavaScript, constructors are the skilled artisans within the castle, crafting new objects and enchanting them with initial properties. Witness their craftsmanship in the magical realm.

// Creating a constructor function
function MagicalCreature(name, type) {
  this.name = name;
  this.type = type;
}

// Enchanting beings with the constructor
const phoenix = new MagicalCreature("Fawkes", "Phoenix");
const dragon = new MagicalCreature("Smaug", "Dragon");

// Behold the enchanted creatures!
console.log(phoenix); // Output: { name: 'Fawkes', type: 'Phoenix' }
console.log(dragon); // Output: { name: 'Smaug', type: 'Dragon' }

⚡ Constructors in Action

  • Creation: Constructors, like skilled artisans, create new instances of objects. In the example, MagicalCreature crafts phoenixes and dragons.

  • Enchantment: The this keyword within the constructor bestows magical properties upon the created beings. Each instance carries a unique name and type.

  • Invocation: The magical invocation of new brings forth new entities. The enchanted creatures, Fawkes and Smaug, come to life.

🧙 The Craft of Constructors

  • Craftsmanship: Constructors encapsulate the art of crafting objects with initial properties.

  • Flexibility: While classes offer a modern approach, constructors showcase the traditional craftsmanship still widely employed.

💡 Wisdom for Apprentices

  • Understanding this: The this keyword within constructors dynamically adapts, pointing to the newly created object. It's the magical force that binds properties to the object.

  • Invocation Ritual: The sacred invocation using new breathes life into the constructor, transforming it into an object-enchanting spell.

🔑 Unveiling the Mysteries of Properties

In the enchanting world of JavaScript, properties play a pivotal role in shaping the characteristics of objects. Let's embark on a journey to uncover the secrets of properties.

🔎 What is a Property?

A property is a magical attribute of an object, defining its characteristics or features. These attributes can be accessed and manipulated, allowing us to interact with the enchanted beings within the realm.

🏡 Property

In the mystical realm of JavaScript, open properties are like gardens accessible to all. They are visible and open for admiration and interaction by anyone in the enchanted kingdom.

class EnchantedBeing {
  power; // Property

  constructor(power) {
    this.power = power;
  }

  revealPower() {
    console.log(`The revealed power: ${this.power}`);
  }
}

const fairy = new EnchantedBeing("Nature Magic");
fairy.revealPower(); // Output: The revealed power: Nature Magic
console.log(fairy.power); // Output: Nature Magic

🔐 Private Property

Amidst the enchanting gardens of open properties, there exist hidden treasures—private properties. Marked by the # symbol, these properties are concealed within the enchanted class or constructor, shielded from external eyes.

// Only on NodeJS

class EnchantedBeing {
  #privatePower; // Private property

  constructor(power) {
    this.#privatePower = power;
  }

  revealPower() {
    console.log(`The hidden power: ${this.#privatePower}`);
  }
}

const sorcerer = new EnchantedBeing("Arcane Magic");
sorcerer.revealPower(); // Output: The hidden power: Arcane Magic
console.log(sorcerer.#privatePower); // Error: Private field '#privatePower' must be declared in an enclosing class

📕 Wisdom from the Tome

  • Open Wonders: Properties without the # symbol are open for all to see and interact with, adding transparency to the enchanted realm.

  • Private Mysteries: Private properties, marked by the # symbol, offer secrecy within the enchanted class or constructor.

✨ Enchanting Methods: The Magic Within

In the enchanted realms of JavaScript, methods are the mystical incantations that bring objects to life, empowering them with the ability to perform magical actions. Let's embark on a magical journey to explore the secrets of methods.

🧙 The Art of Methodology

Methods in JavaScript are functions that are associated with an object. They encapsulate logic and allow objects to perform actions or provide information about themselves. Here's a glimpse of the enchanting syntax:

class EnchantedArtifact {
  // An enchanting method to cast a spell
  castSpell() {
    console.log("Abracadabra! A spell is cast.");
  }

  // A magical method to reveal the enchanted properties
  revealProperties() {
    console.log("Enchanted Properties:", this);
  }
}

// Creating an enchanted artifact
const magicWand = new EnchantedArtifact();

// Casting a spell using the method
magicWand.castSpell();
// Output: Abracadabra! A spell is cast.

// Revealing the enchanted properties
magicWand.revealProperties();
// Output: Enchanted Properties: EnchantedArtifact {}

🔮 The Wisdom of Methods

  • Function Enchantment: Methods are essentially functions that are part of an object, adding a magical touch to the object's abilities.

  • Object Interaction: Methods allow objects to interact with their surroundings, casting spells, revealing secrets, and performing other enchanting tasks.

  • this in Action: Within methods, the this keyword is a magical reference to the object itself, providing a way for objects to refer to their own properties and methods.

🔮 Unveiling the Magic of Getters and Setters

In the enchanting world of JavaScript, getters and setters are magical constructs that bestow objects with the power of controlled access to their properties. Let's uncover the secrets of these mystical guardians.

🔑 Enchanting Access with Getters

Getters are magical methods that allow us to retrieve the value of a property with finesse. They act as guardians, controlling the access to the inner treasures of an object.

class EnchantedArtifact {
  _innerTreasure = "Secret Knowledge";

  // A mystical guardian (getter) revealing the inner treasure
  get treasure() {
    console.log("Accessing the inner treasure!");
    return this._innerTreasure;
  }
}

// Creating an enchanted artifact
const ancientScroll = new EnchantedArtifact();

// Accessing the inner treasure using the getter
console.log(ancientScroll.treasure);
// Output: Accessing the inner treasure!
// Output: Secret Knowledge

🔏 Guarding Secrets with Setters

Setters are enchanting spells that provide a controlled mechanism for setting the value of a property. They ensure that only worthy changes are made to the magical essence of an object.

class EnchantedArtifact {
  _innerTreasure = "Secret Knowledge";

  // A bewitching spell (setter) to safeguard the inner treasure
  set treasure(newTreasure) {
    if (newTreasure === "New Wisdom") {
      console.log("Changing the inner treasure!");
      this._innerTreasure = newTreasure;
    } else {
      console.error("Invalid attempt to change the inner treasure!");
    }
  }
}

// Creating an enchanted artifact
const ancientScroll = new EnchantedArtifact();

// Attempting to change the inner treasure using the setter
ancientScroll.treasure = "Ancient Secrets";
// Output: Invalid attempt to change the inner treasure!
console.log(ancientScroll.treasure);
// Output: Secret Knowledge

ancientScroll.treasure = "New Wisdom";
// Output: Changing the inner treasure!
console.log(ancientScroll.treasure);
// Output: New Wisdom

📕 Wisdom from the Tome

  • Getters' Magic: Getters provide a way to enchantingly retrieve the value of a property, controlling the access to inner secrets.

  • Setters' Spell: Setters act as protective spells, guarding against unworthy changes and ensuring the integrity of an object's magical essence.

  • Guardians of Access: Getters and setters together form the guardians of access, bringing harmony to the magical world of JavaScript.

🤔 The Quest for Setters and Getters: A Journey into Necessity

In the vast realms of JavaScript, the need for setters and getters arises from the desire to create objects with controlled access to their inner properties. This quest leads us to explore the essential reasons behind embracing these enchanting constructs.

🔑 The Key to Controlled Access

  1. Encapsulation of Secrets: Setters and getters enable the encapsulation of an object's internal state. They act as guardians, controlling access to crucial data and preventing unauthorized manipulation.
class MagicalArtifact {
  #innerSecret = "Hidden Wisdom";

  get secret() {
    return this.#innerSecret;
  }

  set secret(newSecret) {
    if (newSecret === "New Wisdom") {
      this.#innerSecret = newSecret;
    } else {
      console.error("Invalid attempt to change the inner secret!");
    }
  }
}

const ancientScroll = new MagicalArtifact();
console.log(ancientScroll.secret); // Output: Hidden Wisdom
  1. Validation and Constraints: Setters empower developers to impose validation checks and constraints when assigning values to properties. This ensures that only valid and acceptable values become part of an object's essence.
class Wizard {
  #magicLevel = 0;

  get magicLevel() {
    return this.#magicLevel;
  }

  set magicLevel(newLevel) {
    if (newLevel >= 0 && newLevel <= 100) {
      this.#magicLevel = newLevel;
    } else {
      console.error("Invalid magic level!");
    }
  }
}

const merlin = new Wizard();
merlin.magicLevel = 75;
console.log(merlin.magicLevel); // Output: 75

🛡️ Safeguarding the Magical Essence

  1. Data Integrity: With setters, the integrity of an object's magical essence is preserved. Unworthy attempts to alter the internal state are thwarted, maintaining the sanctity of the object.

  2. Adaptability and Evolution: Setters provide a flexible mechanism to adapt an object's behavior over time. As the requirements evolve, setters allow for seamless adjustments without compromising the object's integrity.

💡 Illuminating Wisdom

In the enchanting world of JavaScript, setters and getters emerge as indispensable tools for crafting robust and secure objects. They bring forth a balance between accessibility and protection, allowing developers to wield the power of controlled access with finesse.

⚡ The Battle of Titans: Methods vs Getters/Setters

In the grand arena of JavaScript, two formidable contenders—methods and getters/setters—vie for supremacy. Let's explore the strengths and characteristics of these mighty titans to understand when to summon each in the quest for code excellence.

🛠️ The Mighty Methods

  1. Versatility and Power: Methods wield a broad spectrum of capabilities, ranging from executing complex logic to performing a series of actions. They are the swiss army knives of functionality, adaptable to diverse scenarios.
class SpellCaster {
  #spellPower = 0;

  castSpell() {
    // Perform spellcasting logic
    this.#spellPower += 10;
    console.log("Spell cast!");
  }

  getSpellPower() {
    return this.#spellPower;
  }
}

const sorcerer = new SpellCaster();
sorcerer.castSpell();
console.log(sorcerer.getSpellPower()); // Output: 10
  1. Action-Packed: Methods thrive in an action-packed environment. They execute a sequence of steps, making them ideal for scenarios where multiple actions need to be orchestrated.

🔄 The Graceful Getters and Setters

  1. Controlled Access: Getters and setters serve as the gatekeepers of an object's properties, controlling access and modification. They allow for a fine-tuned approach, ensuring that interactions adhere to defined rules.
class EnchantedArmor {
  #defenseRating = 0;

  get defenseRating() {
    return this.#defenseRating;
  }

  set defenseRating(newRating) {
    if (newRating >= 0) {
      this.#defenseRating = newRating;
    } else {
      console.error("Invalid defense rating!");
    }
  }
}

const mysticalArmor = new EnchantedArmor();
mysticalArmor.defenseRating = 20;
console.log(mysticalArmor.defenseRating); // Output: 20
  1. Property Guardians: Getters and setters act as diligent guardians, ensuring that external forces abide by the rules when interacting with an object's properties.

💥 Choosing the Right Champion

  1. Scenario-driven Selection: The choice between methods and getters/setters depends on the scenario at hand. Methods excel in scenarios where dynamic actions and logic are paramount, while getters/setters shine in scenarios demanding controlled property access.

  2. Harmonious Coexistence: In many cases, methods and getters/setters can coexist harmoniously, each contributing its unique strengths to the overall design of the magical artifact.

🏆 Code Sorcery Unleashed!

Congratulations, brave JavaScript sorcerer, for embarking on this enchanting journey through the mystical realms of Object-Oriented Programming in JavaScript. Together, we've explored the arcane arts of prototypes, delved into the secrets of classes, harnessed the power of constructors, and witnessed the dance of getters and setters.

✨ Your Journey Continues

As you emerge victorious from this quest, remember that the world of JavaScript magic is vast and ever-expanding. This is but the beginning of your adventures. The enchanted lands of abstraction, encapsulation, inheritance, and polymorphism await your exploration.

📖 The Tome of Knowledge

Feel free to revisit this magical tome whenever you seek guidance or desire to deepen your understanding of the mystical arts. Each chapter holds the key to unlocking new levels of JavaScript mastery.

🧙 Keep Casting Spells

Armed with the knowledge bestowed upon you, continue casting spells in the form of code. Solve puzzles, tackle challenges, and let your creativity flow. The more you code, the more potent your spells become.

🔮 The Future Awaits

As you traverse the ever-shifting landscape of JavaScript, anticipate new discoveries, challenges, and revelations. The future holds untold possibilities, and with each incantation of code, you shape the destiny of your coding journey.

May your code be elegant, your bugs few, and your algorithms swift. Until we meet again in the realms of code, farewell, fellow sorcerer. May your programming adventures be magical! 🚀✨


Thank you for taking the time to read my article. Your engagement is the driving force behind the words on these pages. Whether you found information, inspiration, or simply enjoyed the content, your presence is deeply appreciated. Writing is a shared journey, and I'm grateful to have you as a reader. Cheers to the joy of exploration and discovery! 🌟

If you enjoyed the article, consider giving it more stars!

With gratitude,

Afzal Imdad