Salem Ouerdani
Salem Ouerdani

Reputation: 7896

extend or inherit an object prototypes when calling a native prototype method

I've been reading Inheritance and the prototype chain and somewhere it sais :

Bad practice: Extension of native prototypes

One mis-feature that is often used is to extend Object.prototype or one of the other built-in prototypes.

This technique is called monkey patching and breaks encapsulation. While used by popular frameworks such as Prototype.js, there is still no good reason for cluttering built-in types with additional non-standard functionality.

The only good reason for extending a built-in prototype is to backport the features of newer JavaScript engines; for example Array.forEach, etc.

What I'm trying to do in a real example is an angular service to handle http requests that returns a simple object instance with few attributes and 2 methods findAll() and findOne(id) that I'll use one of them (and only once) in my ui-router's resolve method :

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 of the 2 methods I'll call, I'm trying to extend (or even replace) the whole instance Resource with another predefined one Item or Collection where each have its own structure and methods so in my Controller I can do stuffs like :

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

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

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

Now, the only working solution I found so far (represented by a minimal example that have nothing to do with http requests) is this :

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

myApp.factory('Animal', Animal);

function Animal() {

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

    Cat.prototype.sayMiau = function() {
        console.log('Miau!')
    };

    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;
}

So inside Controller I can do:

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

And it works as you can see in this jsfiddle.

The Question is :

Is that Correct? I'm not breaking the way how Object.prototype should work or losing the performance gain it should provide? Is there a better way to do it? like maybe using angular.extend or maybe not extending (or replacing) prototype at all and using 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);
    },
};

Upvotes: 2

Views: 345

Answers (1)

wilsotobianco
wilsotobianco

Reputation: 1588

I think the way you are doing it may work. I'm not sure about the factory implementation you did. I did something similar once that may help:

View working jsFiddle here

This is your code with slight edits:

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

myApp.factory('AnimalFactory', AnimalFactory);

function AnimalFactory() {

  // This is the main class
  var Animal = function(age, fullName) {

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

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

  };

  // Dog should inherit from Animal 
  var Dog = function(age, fullName) {

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

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

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

  var Cat = function(age, fullName) {

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

    // Augmenting object adding 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 returned to use factory
  return {
    createDog: createDog,
    createCat: createCat
  };
}

function MyCtrl($scope, AnimalFactory) {

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

  console.log(dan);

  $scope.dan = dan;
}

I think the above code has a cleaner implementation of prototypes inheritance between your classes. Let me know what you think so we can improve it.

Upvotes: 3

Related Questions