When invoking a native prototype method, consider extending or inheriting object prototypes for added functionality

Recently, I came across a discussion on Inheritance and the prototype chain where it mentioned:

Avoiding bad practice: Extension of native prototypes

One common mis-feature is extending Object.prototype or other built-in prototypes.

This practice, known as monkey patching, violates encapsulation. Even though it's utilized by frameworks like Prototype.js, it's still not advisable to clutter built-in types with extra non-standard functionality.

The only acceptable reason for extending a built-in prototype is to retroactively add features from newer JavaScript engines; for instance, Array.forEach.

In a practical example, I'm working on an Angular service that handles HTTP requests returning a simple object with attributes and two methods, findAll() and findOne(id). I intend to use one method in my ui-router's resolve method only:

resolve: {
    Resource: 'Resource',
    allImages: function(Resource) {
      var images = new Resource('images');
      return images.findAll();
    },
    singleImage: function(Resource) {
      var image = new Resource('images');
      return image.findOne(4);
    }
},

Depending on which method I call, I want to extend or replace the entire instance Resource with another predefined one - Item or Collection - each having its structure and methods for usage in the Controller:

if (allImages.existNext()) allImages.nextPage();

var currentPage = allImages.meta.currentPage,
    collection = allImages.data;

singleImage.url = 'abc';
singleImage.save(); 

The most effective solution I found involves a minimal example unrelated to HTTP requests:

var myApp = angular.module('myApp',[]);

myApp.factory('Animal', Animal);

function Animal() {

    var Cat = function() {
        this.preferredFood = 'fish';
    };

    Cat.prototype.sayMeow = function() {
        console.log('Meow!')
    };

    var Dog = function(name) {
        this.dogName = name;
    };

    Dog.prototype.sayGrr = function() {
        console.log('Grrr!')
    };

    function Animal(age) {
        this.age = age;
    }

    Animal.prototype = {
        makeItCat: function() {},
        makeItDog: function(name) {
           Dog.call(this, name);
           this.__proto__ = Dog.prototype;
        },
    };

    return Animal;
}

Therefore, inside the Controller, I can utilize this as follows:

var Dan = new Animal(7);
Dan.makeItDog('Dan');
Console.log(Dan.age); // outputs "7"
Console.log(Dan.dogName); // outputs "Dan"
Dan.sayGrr(); // outputs "Grrr!"

This implementation functions well as showcased in this jsfiddle.

The Query at Hand:

Am I following correct practices? Is there any risk of disrupting how Object.prototype operates or losing potential performance benefits? Could there be a better approach, such as incorporating angular.extend or possibly abandoning prototype extension altogether and utilizing something like this instead:

var Dog = function(name) {
    this.dogName = name;
    this.sayGrr = function(string) {
      console.log('Grrr!')
    }
};
...
Animal.prototype = {
    makeItCat: function() {},
    makeItDog: function(name) {
      Dog.call(this, name);
    },
};

Answer №1

Perhaps the approach you are taking could be effective. I have some reservations about the way you implemented the factory. Here is a modified version that might be helpful:

To see a functional jsFiddle demo, click here

The following code includes some minor adjustments:

var myApp = angular.module('myApp', []);

myApp.factory('AnimalFactory', AnimalFactory);

function AnimalFactory() {

  // Primary class definition
  var Animal = function(age, fullName) {

    this.age = age;
    this.fullName = fullName;

    this.eat = function() {
      console.log('I am eating');
    };

  };

  // Implementing Dog as a subclass of Animal 
  var Dog = function(age, fullName) {

    // Calling the constructor for Animal
    Animal.call(this, age, fullName);

    // Adding a new method to the object
    this.bark = function() {
      console.log('I am ' + this.fullName + ' and I bark Woof woof');
    };
  };

  // Assigning the prototype of Dog to Animal
  Dog.prototype = Object.create(Animal);

  var Cat = function(age, fullName) {

    // Invoking the animal constructor
    Animal.call(this, age, fullName);

    // Extending the object with a new method
    this.meow = function() {
      console.log('I am ' + this.fullName + ' and I meow');
    };
  };

  // Setting the Cat prototype to Animal
  Cat.prototype = Object.create(Animal);

  function createDog(age, fullName) {
    return new Dog(age, fullName);
  }

  function createCat(age, fullName) {
    return new Cat(age, fullName);
  }

  // Interface provided by the factory for usage
  return {
    createDog: createDog,
    createCat: createCat
  };
}

function MyCtrl($scope, AnimalFactory) {

  var dan = AnimalFactory.createDog(7, 'Dan');
  dan.bark();

  console.log(dan);

  $scope.dan = dan;
}

I believe the revised code offers a neater implementation of prototype inheritance among your classes. Please share your thoughts so we can further refine it.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error: Attempting to append a child to a non-existent property

I am currently learning Java Script and this is the code I have been working on. <!doctype html> <html> <head> <style> div {position:absolute; width:500px; height:500px} img {position:absolute} ...

Create a list using ReactJS

I've been working on rendering a dynamic list in JSX, but I'm facing issues displaying the items. Below is my code snippet where I attempted to use useState const [orderList, setOrderList] = useState([]) and setOrderList(prev => [...prev, chil ...

Tips for handling a disabled button feature in Python Selenium automation

When trying to click this button: <button id="btn-login-5" type="button" class="m-1 btn btn-warning" disabled="">Update</button> I need to remove the disable attribute to make the button clickable. This ...

utilizing geographical coordinates in a separate function

I have a program that enables users to access the locations of communication cabinets. I am attempting to utilize html5 to transmit latitude and longitude information to a php script (geo.php) in order to update the database record with map coordinates. Ho ...

Testing API route handlers function in Next.js with Jest

Here is a health check function that I am working with: export default function handler(req, res) { res.status(200).json({ message: "Hello from Next.js!" }); } Alongside this function, there is a test in place: import handler from "./heal ...

The response from a jQuery ajax call to an MVC action method returned empty

I am working on an inventory application with the following layout: <body> <div class="container" style="width: 100%"> <div id="header"> blahblahblah </div> <div class="row"> <div id="rendermenu ...

The module cannot be required as a function for calculating the area of a square

None of the functions I created seem to be working properly. Take a look at this example function: function calculateArea(side) { var area = side * side; return area; } When I attempt to use the module using require, like so: var formulas = require( ...

GWT Validation - Enhance Your Application with a Third Party Library

Is there a reliable JavaScript library available for validating GWT fields such as Email, Text Length, Phone Number, Date & SSN, etc.? I am unable to use the GWT Validation Framework or GWT Errai in my application because I receive responses as JSON r ...

Gauging Screen Size: A Comparison between Media Queries and JavaScript for Adjusting Div Position

I am currently facing an issue with the banner on my website. It contains a slider and has a position set to absolute. The problem arises when viewing it on smaller screens, as only the left side of the wide banner is visible. Initially, I tried using med ...

Angular Chart.js is throwing an error: "Uncaught SyntaxError: Cannot use import statement outside a module"

Upon opening the page, an error in the console related to Chart.js 4.2.1 is being displayed. Description of first image. Description of second image. Is it possible that this issue solely lies with Chart.js? How can it be resolved? To address the proble ...

Error: The value is null and cannot be read

My external application is set up within a const called setupRemote, where it starts with the appConfig in the variable allowAppInstance. export const setupRemote = () => { if (isRemoteAvailable) { try { ... const allowAppInstance = S ...

The module '@algolia/cache-common' is missing and cannot be located

summary: code works locally but not in lambda. My AWS lambda function runs perfectly when tested locally, utilizing Algolia within a service in the server. Despite installing @algolia/cache-common, any call to the lambda results in a crash due to the erro ...

Save pictures in MongoDB using GridFS or BSON format

As a newcomer to MongoDB, I am seeking advice on the best way to store images in the database. Gridfs and BSON seem to be the most common options, but I'm unsure about their respective pros and cons. The main difference I'm aware of is the 16MB s ...

When the condition fails to activate

I am encountering an issue with my code that involves an if statement checking the value of a variable and binding a mousewheel event based on its true or false value. The problem is, the if condition only triggers on load and not when the value changes to ...

Access the modal by simply clicking on the provided link

I have implemented a code snippet below to display data from MySQL in a modal popup. Everything is functioning correctly, but I am interested in triggering the modal by clicking a link instead of a button. Is it feasible to achieve this considering I have ...

Troubleshooting a mysterious anomaly with a jQuery UI button

Would like to achieve something similar to this: http://jqueryui.com/demos/button/#icons When trying to replicate it, http://jsfiddle.net/p5PzU/1/ Why is the height so small? I expected a square shape but am getting a rectangle instead. What could be c ...

What is the best way to access the front camera on both Android and iOS devices in order to capture a photo using Vue.J

I am currently developing a PWA Vue.Js application and I am trying to implement a feature that allows users to take a picture with the front camera on their mobile devices. Although I have managed to write code that works on my desktop browser, I have bee ...

Learn how to upload an image using Vue.js and then trigger a custom method

Greetings! I am a newcomer to Vue.js and I have encountered a problem that I need help with. In my component, there is a hidden input for files. When I click a button, the browser displays a window for importing files from my PC. Once I choose a file, I wa ...

Javascript object attributes

Could you retrieve the value of one object property based on the value of another property? For instance, in an SQL query, is it possible to fetch the id from the object where the object.name equals "somename"? I am trying to obtain the id of a particula ...

I currently have two responsive menus and I'm trying to figure out how to modify the javascript so that when one menu is opened, the other

I am facing an issue with my responsive menus on a webpage, similar to the example provided in the jsfiddle link below. Currently, when one menu is open and I click on another, both remain open. How can I modify the JavaScript code so that when one menu op ...