It seems to me that you are thinking Javascript as a language oriented objects according to the class paradigm. This is not the case. In Javascript there are no classes. Everything is an object, which is like a map
(or if you come from Python, a dictionary
) of properties with their values.
var objeto1 = {nombre : 'yo', edad : 30};
var objeto2 = {nombre : 'ella', edad : 29};
function imprimirObjeto(o) {
console.log(o);
}
imprimirObjeto(objeto1);
What you need to do is:
-
Everything object has a hidden property (the "prototype"). When I try to read a property in an object and it does not, it tries to read it in the "prototype". (The prototype can also have another prototype, and so ...)
-
The prototype can not be accessed directly.
-
A function is also an object (with a special prototype, but that does not matter for now).
-
There are functions that are designed to work as something-so-like "builders." They usually start (by convention) with a capital letter, and are not usually explicitly invoked as other functions, but only with the keyword new
.
When executing
function Casa() {
this.habitaciones = 2;
...
}
var c1 = new Casa();
var c2 = new Casa();
Javascript manufactures two new objects stored in variable c1
and c2
. Informally, we think that "we instantiate two houses"; correct, but do not confuse the object Casa
(the constructor function, which is only one) with the objects c1
and c2
that are two objects created by Casa()
and that no are copies of Casa
).
(Maybe it helps a bit to understand it if we mentally rename Casa()
to fabricarCasa()
, and instead of c1 = new Casa()
we imagine c1 = fabricarCasa()
This is how we would do it in other languages, but in Javascript it is not possible, and it's important not to get confused: you have to use new
for a construction function to work as such, and if we forget, we'll see ugly things happen.
What is particular about this is that
-
The keyword this
in a "constructor" will refer to the new object created in the call with new
-
A constructor has a special attribute called prototype
(by default, an empty object); the value of this prototype
will be assigned as a prototype of each new created object.
Eye! (And this is what is often very confusing at first): Casa.prototype
is not the prototype of the object Casa
! It is the prototype that will be assigned (not by copy, but by reference) to the objects created by the function Casa
!
- The
prototype
of a constructor is any object, and I can modify it when I want. The change will impact (dynamically) on the objects created by the constructor (before and after the modification)
Let's see if with an example it is understood (execute and look at the console).
// esta es un funcion que se usará como constructor
function Estadio(nombre, capacidad) {
this.nombre = nombre;
this.capacidad = capacidad;
}
// modificamos directamente el objeto prototype de la funcion constructora , agregando un atributo
// notar que Estadio.prototype no es el prototipo de Estadio
Estadio.prototype.utilidad = 'x';
// creamos dos objetos
// estos objetos tendran como prototipo (oculto) el objeto (unico) Estadio.prototype
var estadio1 = new Estadio('Estadio 1',20);
var estadio2 = new Estadio('Estadio 2',30);
console.log("Nombre de estadio 1:" + estadio1.nombre);
console.log("Nombre de estadio 2:" + estadio2.nombre);
// estas propiedades no estan en cada objeto, pero sí en el prototipo
console.log("Utilidad de estadio 1:" + estadio1.utilidad);
console.log("Utilidad de estadio 2:" + estadio2.utilidad);
// cambiamos el atributo prototype de la funcion Estadio
// (que a su vez está refernciado en el prototipo oculto
//de los dos objetos anteriores
Estadio.prototype.utilidad = 'y';
// vemos que ese cambio "lo ven" los objetos
console.log("Utilidad de estadio 1:" + estadio1.utilidad);
console.log("Utilidad de estadio 2:" + estadio2.utilidad);
// como el atributo no estaba en el objeto (sino en el prototipo), se crea
// en el objeto (la cadena de prototipos se usa para leer, nunca se escribe)
estadio1.utilidad = 'y1';
// ver que ahora los objetos ven diferentes atributos
console.log("Utilidad de estadio 1:" + estadio1.utilidad);
console.log("Utilidad de estadio 2:" + estadio2.utilidad);
// moficiamos nuevamente el prototipo "global"
Estadio.prototype.utilidad = 'z';
// eso solo incide en el segundo objeto, porque el primero lo tiene
console.log("Utilidad de estadio 1:" + estadio1.utilidad);
console.log("Utilidad de estadio 2:" + estadio2.utilidad);
Regarding "create objects with common properties". You have to distinguish: do you want all of them to have the same set of properties (but perhaps with different values) or do you want them to share the values?
Usually the constructor (as in my examples and yours) define a set of properties with their values. Each object created by that constructor will have those properties with (a copy of) those values, which eventually we can change.
We could achieve something similar by assigning those properties to a prototype.
Specifically, compare
function Casa() {
this.habitaciones = 2;
}
var c1 = new Casa();
var c2 = new Casa();
with
function Casa() {
}
Casa.prototype.habitaciones = 2;
var c1 = new Casa();
var c2 = new Casa();
The result will be almost indistinguishable, both to read c1.habitaciones
and to write it.
The advantage of the second (attributes in the prototype) is that it is more efficient in memory management, because if I create 10 objects I do not have 10 copies of the attribute habitaciones
but (as long as it does not change it) only one, the one that lives in the prototype.
This efficiency is VERY important in some Javascript applications, in particular to manage the DOM (object tree of a web page) and events.
Another difference is that the second procedure is more flexible because if, once the 10 objects are created, I modify the prototype, the 10 objects (as long as they do not write it) will see the attribute reflected. Of course this, in some cases, can be a disadvantage.