En JavaScript todo son objetos

JavaScript se maneja con objetos aunque el programador puede que no necesite saberlo. Algunos objetos intrínsecos del lenguaje son Array, Boolean, Debug, Date, Enumerator, Error, Math, Number, RegExp, String. Pero además hay tres objetos que ahora nos interesan: Function, Global y Object.

JavaScript convierte los objetos Function según el contenido de su cuerpo. Si se declaran sin la palabra clave this los trata como funciones que pueden devolver valores. Si se incluye esta palabra los trata como objetos, es decir, clases de objetos, pudiéndose instanciar con la palabra clave new.

El objeto Global no se usa directamente sino que es creado cuando se inicializa el sistema. Se trata de un objeto para recoger unos métodos como decodeURI(), isNaN() o parseInt(), métodos a los cuáles podrán acceder el resto de objetos.

El objeto Object está contenido en el resto de los demás objetos. Por ejemplo, una de sus propiedades es prototype y uno de sus métodos es toString(). Para el resto de objetos incluso los creados por el programador, sus propiedades y métodos están disponibles.

Los objetos creados por el programador con el objeto Function

En el tema anterior creamos el objeto de ejemplo coche que volvemos a reproducir aquí. El siguiente código muestra la clase con la cual definíamos en abstracto un coche que volvemos a reproducir aquí, que está situado en un archivo externo a esta página:

function coche(unaMarca, unModelo, unColor) {
    this.marca = unaMarca;
    this.modelo = unModelo;
    this.color = unColor;

    this.seleccionarMarca = function seleccionarMarca(otraMarca){
        this.marca = otraMarca
    };

    this.seleccionarModelo = function seleccionarModelo(otroModelo){
        this.modelo = otroModelo
    };

    this.seleccionarColor = function seleccionarColor(otroColor){
        this.color = otroColor
    };

    this.verComoEs = function verComoEs(){
        return "Marca: " + this.marca + "<br />" +
        "Modelo: " + this.modelo +  "<br />" +
        "Color: " + this.color;
    };

}

Luego podíamos crear múltiples instancias de esa clase:

<div id="div1"></div>
<script>
    var miPrimerCoche = new coche("Volkswagen", "Golf", "Rojo");
    document.getElementById("div1").innerHTML = miPrimerCoche.verComoEs();
</script>
Actualización NOV 2012: Con los últimos cambios he decidido que todos los scripts vinculados desde archivos externos se carguen asíncronamente. Por lo tanto el código del script anterior y el resto de esta página que necesiten el archivo objetos.js cargado, los he tenido que traspasar al window.onload de esta página, momento en el cual estará disponible el constructor ubicado en el archivo vinculado objetos.js.

Ejemplo:

"Así es miPrimerCoche (una instancia del objeto coche)"

Con lo anterior habíamos creado un objeto nuevo usando el objeto de JavaScript Function. Con el uso de la palabra clave this podíamos dotarle de nuevas propiedades. Incluíamos nuevas funciones en su interior que venían a ser nuevos métodos para ese objeto creado. Pero hay dos formas de crear un nuevo objeto Function:

  • Con la forma implícita que hemos usado antes:
    function nombre_funcion (argumento1, argumento2, ...) {
        this nombre_propiedad 1 = argumento1;
        this nombre_propiedad 2 = argumento2;
        ...
    
        this.nombre_metodo1 = function nombre_metodo1(a, b, ...) {
    
        };
        ...
    }
  • Usando la forma explícita con el objeto Function en lugar de la declaración function (vea la diferencia en la letra F/f):
    var nombre_funcion = new Function(
        "argumento1",
        "argumento2",
        ...,
        "this.nombre_propiedad1 = argumento1;" +
        "this.nombre_propiedad2 = argumento2;" +
        ...
        "this.nombre_metodo1 = function nombre_metodo1(a, b, ...){...};" +
        ...
        ")"
    
    )

Esta segunda forma explícita viene a realizar lo mismo que la anterior, pero no conviene usarla para el propósito de crear objetos debido a la complejidad de escribir el código. Veáse que los nombres de los argumentos van entre comillas separados por comas, siendo el último el cuerpo de la función. Todas estas cadenas son tratadas como código que se compila y ejecuta en el momento en que se llama a la función, por lo que resulta realmente ineficiente.

El tema de las separaciones de sentencias es muy crítico en este punto. Sabemos que con JavaScript se pueden obviar los ";" al final de la sentencia si esta coincide con un salto de línea. Pero cuando escribimos "código literal" como en este caso, es necesario incluir todos los ";", incluso después de cada cuerpo "{...};".

Cómo dijimos antes, debido a su ineficiencia, esta forma explícita se usa para declarar funciones de una forma rápida y cuyo cuerpo de código sea más bien simple. Por ejemplo: var sumaDoble = new Function("a","b", "return 2*(a+b)") podría ser una línea de código incrustada entre otras tantas de tal forma que luego podemos llamar a esta función (por ejemplo sumaDoble(2,4)) y no tener que declararla en un cuerpo function sumaDoble(a,b){return 2*(a+b)} separado del flujo de sentencias que estamos escribiendo.

De todas maneras aplicamos esta forma explícita a nuestro ejemplo, creando una variable llamada coche2 y declarando la función en un archivo externo:

var coche2 = new Function("unaMarca", "unModelo", "unColor",
"this.marca = unaMarca;" +
"this.modelo = unModelo;" +
"this.color = unColor;" +
"this.seleccionarMarca = function seleccionarMarca(otraMarca){" +
"this.marca = otraMarca; };" +
"this.seleccionarModelo = function seleccionarModelo(otroModelo){" +
"this.modelo = otroModelo; };" +
"this.seleccionarColor = function seleccionarColor(otroColor){" +
"this.color = otroColor; };" +
"this.verComoEs = function verComoEs(){" +
"return \"Marca: \" + this.marca + \"<br />\" + " +
"\"Modelo: \" + this.modelo + \"<br />\" + " +
"\"Color: \" + this.color " +
"}")

Como en el caso de la forma ímplicita, podemos crear múltiples instancias de este objeto coche2: en esta página:

<div id="div2"></div>
...
<div id="div3"></div>
<script>
    var miCoche1 = new coche2("Toyota", "Avensis", "Plata");
    var miCoche2 = new coche2("BMW", "F5", "Negro");
    document.getElementById("div2").innerHTML = miCoche1.verComoEs();
    document.getElementById("div3").innerHTML = miCoche2.verComoEs();
</script>
    

Ejemplo:

"Este es miCoche1 como una instancia del objeto coche2"
"Y este es miCoche2, otra instancia de coche2"

Podemos actuar sobre el método seleccionarModelo() de la instancia micoche2

Ejemplo:

"Cambiamos el modelo de miCoche2"

El operador instanceof

Este operador nos permite saber si un objeto cualquiera (intrínseco de JavaScript o creado por nosotros) es una instancia de una determinada clase. Ponemos este ejemplo para comprobar los objetos que hemos creado (miPrimerCoche, miCoche1, miCoche2) si son instancias de sus clases (coche y coche2) y también probamos algunas clases intrínsecas de JavaScript:

<div id="div5"></div>
<script>
    var cadena = "";
    cadena = "¿miPrimerCoche es una instancia de coche?: " + (miPrimerCoche instanceof coche);
    cadena += "</br >¿miCoche1 es una instancia de coche?: " + (miCoche1 instanceof coche);
    cadena += "</br >¿miCoche1 es una instancia de coche2?: " + (miCoche1 instanceof coche2);
    cadena += "</br >¿miCoche2 es una instancia de coche2?: " + (miCoche2 instanceof coche2);
    var unaCadena = new String("abc");
    var unNumero = new Number(123);
    cadena += "</br >¿La cadena \"abc\" es una instancia de String?: " + (unaCadena instanceof String);
    cadena += "</br >¿El número 123 es una instancia de String?: " + (unNumero instanceof String);
    cadena += "</br >¿El número 123 es una instancia de Number?: " + (unNumero instanceof Number);
    document.getElementById("div5").innerHTML = cadena;
</script>
    

Ejemplo:

El objeto Object de JavaScript: objeto de objetos

Este título parece un trabalenguas, pero lo que quiere decir es que los demás objetos tanto los intrínsecos de JavaScript como los creados por el programador, tiene a este Object como objeto del cual heredan sus propiedades y métodos. Veámos con detalle cuáles son las más importantes:

  • Propiedades:
    • prototype
    • constructor
  • Métodos:
    • isPrototyeOf()
    • hasOwnProperty()
    • toString()
    • valueOf()

Para demostrar lo que hemos dicho de que cualquier objeto creado hereda estas propiedades y métodos de Object, lo aplicamos a los objetos que creamos antes así como a otros objetos intrínsecos de JavaScript para, por ejemplo, ver si ejecutan los tres últimos métodos señalados hasOwnProperty(), toString() y valueOf() :

<div id="div6"></div>
<script>
    var cadena = new String("abc");
    var numero = new Number(123);
    miCoche2.nuevaPropiedad = "xxx";
    var cad  = "miPrimerCoche.toString() = " + miPrimerCoche.toString() + "<br />" +
    "miPrimerCoche.valueOf() = " + miPrimerCoche.valueOf() + "<br />" +
    "miPrimerCoche.hasOwnProperty(\"marca\") = " + miPrimerCoche.hasOwnProperty("marca") + "<br />" +
    "miPrimerCoche.hasOwnProperty(\"prototype\") = " + miPrimerCoche.hasOwnProperty("prototype") + "<br />" +
    "miCoche2.hasOwnProperty(\"nuevaPropiedad\") = " + miCoche2.hasOwnProperty("nuevaPropiedad") + "<br />" +
    "cadena.toString() = " + cadena.toString() + "<br />" +
    "cadena.valueOf() = " + cadena.valueOf() + "<br />" +
    "numero.toString() = " + numero.toString() + "<br />" +
    "numero.valueOf() = " + numero.valueOf();
    document.getElementById("div6").innerHTML = cad;
</script>
    

Creamos dos objetos intrínsecos cadena y numero. Además agregamos una nuevaPropiedad al objeto miCoche2. Luego con el método miPrimerCoche.hasOwnProperty("nuevaPropiedad") comprobamos que es cierto que esa es una propiedad que pertenece al objeto. Los métodos toString() y valueOf() devuelven una cadena y un valor, que para un objeto creado siempre será [object Object], aunque para los intrínsecos como los del ejemplo devolverá el valor en sí mismo.

Ejemplo:

La clase Object también nos sirve para crear un objeto directamente sin usar function tal como hicimos para nuestra clase coche.

<div id="div7"></div>
<div id="div8"></div>
<script>
    var cocheObjeto = new Object();
    cocheObjeto.marca = "";
    cocheObjeto.modelo = "";
    cocheObjeto.color = "";
    cocheObjeto.configurar = function configurar(unaMarca, unModelo, unColor) {
        this.marca = unaMarca;
        this.modelo = unModelo;
        this.color = unColor;
    };
    cocheObjeto.verComoEs = function verComoEs(){
        return "Marca: " + this.marca + "<br />" +
        "Modelo: " + this.modelo +  "<br />" +
        "Color: " + this.color;
    };
    cocheObjeto.configurar("Opel", "Corsa", "Marrón");
    document.getElementById("div7").innerHTML = cocheObjeto.verComoEs();
    document.getElementById("div8").innerHTML = "¿<code>cocheObjeto</code> es una instancia " +
    "de <code>coche</code>? " + (cocheObjeto instanceof coche) + "<br />" +
    "¿<code>cocheObjeto</code> es una instancia de <code>Object</code>? " +
    (cocheObjeto instanceof Object);
    </script>
    

Ejemplo:

"Un objeto cocheObjeto creado con la clase Object"

Esto no añade ninguna utilidad nueva, dado que tendríamos que usar este código con cada variable nueva de coche para cada objeto que quisiéramos crear. La importancia de la clase Object está en su propiedad prototype, que está presente en todos los objetos, o mejor dicho, en sus clases, tanto las intrínsecas como las creadas por el programador.