Reputation: 7896
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.
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
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