The problem with doing this in Javascript is that - at least until Ecmascript 5 (the version currently most supported by browsers) - This language has no classes! While in PHP, or Java, C#, etc "class inherits from class" and "class instance object", in Javascript "object inherits from object".
So what?
It may seem a theoretical detail without much importance (amplified by the fact that Javascript "pretend" that supports Classical OO), but it has a very precise practical significance: the inherited object does not have a copy attributes of the original object, both share the same attributes! This means that if the attribute of the parent object is mutable, and you try to change it in the child object, you are changing the same object.
Another consequence of this mode of inheritance (which is called "prototypical inheritance") is that it does not give the attributes of the "subclass" to inherit the default value of the attributes of the "superclass" - a specific instance of the "superclass" is required for the inheritance to be actually possible:
function Superclasse(foo) {
this.foo = (typeof foo == "undefined" ? "bar" : foo);
}
function Subclasse() { }
Subclasse.prototype = new Superclasse("baz");
var obj = new Subclasse(); // Não herda de "Superclasse", mas de uma instância
// específica de "Superclasse"
console.log(obj.foo); // baz
In this simple example the solution is obvious: do not pass the parameter "baz"
in the prototype builder (leave it with the default value) and make the constructor of Subclasse
call the builder of Superclasse
. But in more complex situations it is necessary to be careful, especially because this prototype is a complete object, it can be accessed, have its methods called, etc.
That my answer the other question on the subject addresses other potential problems with Javascript inheritance. Perhaps because of this confusing nature, I don’t know, it was decided that Ecmascript 6 would provide appropriate class support, as exemplified in phb response. But until then...
How then?
You can do it in the "traditional" way (i.e. imitating classical heritage) or in the "explicit" way (making it clear what you are inheriting from that). I personally always prefer the explicit mode, but I often use the traditional for reasons of force majeure (some time ago this method had even better performance in Chrome than the explicit).
"Builders" and new
If you call a function f
anyone using the keyword new
, the following things happen:
- A new object is created;
- Your prototype becomes
f.prototype
(that by the way is not the prototype of f
!);
- The function
f
is called, using this new object as the this
;
- The return of the function is:
- ignored, if not of the type
object
(!) and the new
returns this new object; or:
- returned, if the type
object
, ignoring the this
newly created (!!).
Confused? A little, but if you follow the "magic formula" everything seems to work well:
// Cria uma função com o papel de "construtora"
function Animal(nome) {
this.nome = (typeof nome == "undefined" ? "Anônimo" : nome);
//return 42; // Não retorne nada
}
// "prototype" é o protótipo dos objetos criados por esse construtor
Animal.prototype.reino = "Animalia";
Animal.prototype.descricao = function() {
return this.nome + " é um membro do reino " + this.reino;
}
function Cachorro(nome, cor) {
Animal.call(this, nome);
this.pelos = cor;
}
Cachorro.prototype = new Animal(); // O protótipo dos cachorros é uma instância de animal
Cachorro.prototype.familia = "Canidae";
Cachorro.prototype.descricao = function() {
return this.nome + " é um membro do reino " + this.reino +
", família dos " + this.familia + ". Seu pelo é " + this.pelos;
}
function Calopsita(nome, cor) {
Animal.call(this, nome);
this.penas = cor;
}
Calopsita.prototype = new Animal(); // idem, mas note que é uma instância diferente
Calopsita.prototype.ordem = "Psittaciformes";
Calopsita.prototype.descricao = function() {
return this.nome + " é um membro do reino " + this.reino +
", ordem dos " + this.ordem + ". Suas penas são " + this.penas;
}
// Criar as "classes" pode ser difícil, mas instanciar é super fácil
var animais = [
new Cachorro("Sebastian", "branco"),
new Calopsita("Rex", "amarelas"),
new Animal("pombo que pousou na minha janela")
];
for ( var i = 0 ; i < animais.length ; i++ )
document.body.innerHTML += "<p>" + animais[i].descricao() + "</p>";
If you want to extend a "class", you can do it even if there are already "instances" of it, simply modifying the object that was created as a prototype:
Calopsita.prototype.clade = "Dinossauria";
Object.create
When you call Object.create
, a new object is created, with the prototype informed (you can use null
if you do not want any prototype, but for equivalence with a literal object, use Object.prototype
) and optionally with a set of property descriptors. Simple and direct:
// Literal de objetos (mais simples; seu protótipo é Object.prototype)
var animal = {
nome: "Anônimo",
reino: "Animalia",
descricao: function() {
return this.nome + " é um membro do reino " + this.reino;
}
};
// Object.create (o chato são esses "descritores de propriedades"...)
var cachorro = Object.create(animal, {
familia: { value:"Canidae" },
descricao: { value: function() {
return this.nome + " é um membro do reino " + this.reino +
", família dos " + this.familia + ". Seu pelo é " + this.pelo;
}}
});
var calopsita = Object.create(animal, {
ordem: { value: "Psittaciformes" },
descricao: { value: function() {
return this.nome + " é um membro do reino " + this.reino +
", ordem dos " + this.ordem + ". Suas penas são " + this.penas;
}}
});
// Instanciar, no entanto, é bastante verboso...
var animais = [
Object.create(cachorro, { nome: { value:"Sebastian" }, pelo: { value: "branco" } }),
Object.create(calopsita, { nome: { value:"Rex" }, penas: { value:"amarelas" } }),
Object.create(animal, { nome: { value:"pombo que pousou na minha janela" } })
];
for ( var i = 0 ; i < animais.length ; i++ )
document.body.innerHTML += "<p>" + animais[i].descricao() + "</p>";
Similarly, members added to the prototype reflect on inherited objects:
calopsita.clade = "Dinossauria";
Etc.?
These are not the only two ways to extend an object in Javascript: there are others building patterns, some simpler and others more complex, but all end up falling into one of these two forms (new
or Object.create
). It is not worth further detailing the matter here, even by what inheritance is complex enough in classical languages, in Javascript further, better structure your code via composition whenever possible, only using inheritance when strictly necessary.
Extending native types
Okay, after this whole turn, your original doubt seems not to have been addressed: can you extend native Javascript types like numbers, strings and arrays? The answer is... yes, but avoid doing that rsrs!
The fact that the inheritance is prototypical makes it trivial to add new fields and methods in the basic types, just modify the field prototype
of its builders (who, reiterating once again, is not the prototype of the same, but rather the prototype of the objects created by them via new
):
Number.prototype.quadrado = function() {
return this * this;
};
document.body.innerHTML += (10).quadrado();
This is often used to extend basic types with useful functions. Used too much. Abused, I would say. In fact, I don’t trust any code that does this, because you never know which members have been added/modified, if one has overwritten another that already existed, etc. This pollutes the namespace, in the same way that global variables would pollute, so that I personally do not recommend - as much as the idea is useful (there are better ways for a language to support this, see Extension methods of the C#).
The "second best thing" would then be to create subtypes of these basic types; something that seem with a string, but built in a "special" way and only objects of the same type have customized members. Is there any way? Well, I myself already beat myself up with this idea a while ago, and I gave up, because though at first this is possible the construction of the basic types actually involves a lot of "magic". See for example the case of "wear or not new
when building an array", and how the situation changes when you build a string... So even though it’s a useful concept, it’s not widely used in practice, and maybe it’s best to keep it that way (if you want some custom type to have all the methods of Array
, for example, simply copy them to your prototype! See the concept of mixins).
Wallace, Mozilla has a section explaining all this in the smallest detail and very well explained https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain
– Erick Gallani
Related: When to use prototype and This is a correct example of Javascript inheritance?
– rray
Thanks @Erickgallani. I’ll take a look
– Wallace Maxters