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