Profundizando en Javascript, parte 2: objetos, prototipos, herencia y namespaces

Tras un tiempo de descanso continuamos con la serie “Profundizando en Javascript”. En el post anterior explicaba todas las maneras de definir y usar funciones: con nombre/sin nombre, como expresión/como declaración, anónimas autoejecutables y anidadas (aunque no es obligatorio, es recomendable haber leído antes de continuar).

Hoy vamos a ver como se trabaja con objetos. En Javascript, como en cualquier lenguaje, un objeto no es más que un conjunto de atributos y funciones (sus métodos). A cada objeto podemos añadirle nuevas propiedades y funciones de diferentes maneras que iremos viendo. Sin embargo la herencia, tal y como la conocemos de otros lenguajes como Java, no existe, pues no existen las clases: para crear un nuevo objeto que tenga los mismos atributos que otro, estos deben ser copiados. A esta forma de trabajar se llama herencia basada en prototipos. Esto significa que no hay clases o interfaces como en Java o C++: solo hay objetos que copian (heredan) sus propiedades de otros objetos, llamados prototipos. Así que olvidémonos de la clásica definición de “objeto como instancia de una clase”, porque aquí no hay clases (y por esta razón, cuando hable de clases en Javascript, lo haré entre comillas así: “clases”). Pero vayamos por partes:

Creando objetos

Pare crear un nuevo objeto podemos hacerlo de cualquiera de estas dos formas:

var obj1 = new Object()
var obj2 = {}

La segunda forma se llama “objeto literal”. Es la que usamos para crear lo que en otros lenguajes se llaman diccionarios, mapas o hashtables y que sigue, afortunadamente, la misma sintaxis que JSON.

Para añadir una nueva propiedad, solo tenemos que asignársela:

var obj1 = new Object()
obj1.nombre = "mundo"

alert("hola "+obj1.nombre) // "hola mundo"

Para eliminar una propiedad, usaremos el operador delete:

var obj1 = new Object()
alert(obj1.nombre)   // undefined

obj1.nombre = "mundo"
alert(obj1.nombre)   // "mundo"

delete obj1.nombre

alert(obj1.nombre)   // undefined otra vez

Y para añadir un método, solo hay que añadirle una función como expresión, teniendo siempre en cuenta que para que un método pueda acceder a las propiedades del objeto donde reside, tenemos que usar el prefijo this.

var obj1 = new Object()
obj1.nombre = "mundo"
obj1.saluda = function() { alert("hola "+this.nombre) }
obj1.saluda()

Aquí vemos como es necesario usar this.nombre dentro de la función saluda() para acceder al atributo nombre del objeto.

Otra forma de construir objetos es como literales:

var obj1 = {nombre:"mundo",
            saluda: function() { alert("hola "+this.nombre) }
}
obj1.saluda()

En Javascript podemos acceder a las propiedades de un objeto usando la notación objeto.propiedad; o de manera indexada como un array, con objeto[prop]. En este segundo caso, prop es una variable de tipo String cuyo contenido es el nombre de la propiedad a acceder. Ejemplo:

var obj1 = new Object()
obj1.nombre = "mundo"
obj1.saluda = function() { alert("hola "+this.nombre) }

alert("hola "+obj1.nombre)       // Acceso normal
alert("hola "+obj1[ "nombre" ])  // Acceso indexado con un literal
var prop = "nombre"
alert("hola "+obj1[ prop ])      // Acceso indexado con una variable

obj1.saluda()         // Invocación normal
obj1[ "saluda" ]()    // Acceso indexado con un literal
var func = "saluda"
obj1[ func ]()        // Acceso indexado con una variable

Podemos usar esta segunda forma de acceder a las propiedades de un objeto para listarlas todas, sin importar cuantas tenga ni su nombre:

var obj1 = new Object()
obj1.nombre = "mundo"
obj1.fecha = new Date()
obj1.luckyNumbers = [4,8,15,16,23,42]
obj1.saluda = function() { alert("holas "+this.nombre) }

var name
for (name in obj1) {
	alert(name+" : "+ obj1[name])
}

Prototipos

Pero claro, con esto solo definimos un único objeto, con sus atributos y métodos. Pero, ¿qué pasa si queremos crear una “clase” de la que poder instanciar objetos? Como hemos dicho antes, en Javascript no hay clases, sino prototipos: objetos de los que se copian otros objetos. Así que lo que haremos es crear un objeto del que poder copiar sus propiedades. Para esto Javascript tiene un gran truco: hace que cualquier función pueda ser instanciada como un nuevo objeto usando la palabra clave “new”.

// Una simple función
function Saludator() {
	alert("Saludos factory!!")
}
// Y una simple llamada
Saludator()

A simple vista no es más que una función, ¡y es verdad que lo es! pues podemos llamar a Saludator() como una función de toda la vida. Pero eso no quita que sea también una factoría y que con ella podamos crear nuevos objetos:

// Una función... pero también un prototipo!
function Saludator() {
	alert("Saludos factory!!")
}
// Un nuevo objeto a partir de su "clase" prototipo
var saludo = new Saludator()

Pero claro, con este ejemplo tan sumamente sencillo no se aprecia que hemos creado un nuevo objeto. Así que vamos a añadirle un par de propiedades y métodos (recordemos que solo tenemos que declararlas con el prefijo this).

function Saludator(nom) {
	alert("Saludos factory!!")
	this.nombre = nom+"s"
	this.saluda = function() {
		alert("hola "+this.nombre)
	}
}
var obj1 = new Saludator("mundo")
var obj2 = new Saludator("gente")
obj1.saluda() // "hola mundos"
obj2.saluda() // "hola gentes"

En este ejemplo ya vemos cosas nuevas:
– Que la función Saludator() es el constructor del objeto, y podemos pasarle parámetros que posteriormente podemos usar en los atributos del objeto.
– Que dentro de la función saluda sigue siendo necesario usar this.nombre para acceder al atributo nombre.

Por cierto, podemos crear nuestros prototipos usando funciones como expresiones, y no como declaraciones. El siguiente código es completamente equivalente al anterior:

var Saludator = function(nom) {
	alert("Saludos factory!!")
	this.nombre = nom+"s"
	this.saluda = function() {
		alert("hola "+this.nombre)
	}
}

Es necesario señalar que las funciones que servirán para crear objetos se suelen nombrar con la primera letra en mayúscula. Esto es así para evitar que se confundan con funciones normales o variables.

Métodos privados

Podemos crear métodos “privados” (es decir, métodos que son solo accesibles desde dentro del objeto, nunca desde fuera), creando funciones como declaración, no como expresión. La principal desventaja es que estos tipos de métodos no tienen ligado el “this” con el objeto actual, por lo que no pueden acceder a sus datos internos. Para evitar esto, tenemos que enviar como parámetros los datos del objeto que necesiten. Además, no podemos usar el prefijo this para llamar a estos métodos:

var Saludator = function(nom) {
	this.nombre = nom
	this.saluda = function() {
		alert("hola "+plural(this.nombre))
	}
	function plural(n) {
		return n + "s"
	}
}
var obj1 = new Saludator("mundo")
obj1.saluda()
// obj1.plural()  // fallaría porque plural() es privado

La llamada al método plural() no es accesible desde fuera, por lo que fallaría (por eso está comentada). Observemos también como la llamada a plural() no se hace con this.plural(). Y que para acceder al atributo nombre hemos tenido que pasárselo como parámetro.

Los métodos privados no son dinámicos, en el sentido que no se pueden definir condicionalmente en tiempo de ejecución. Así que el siguiente código no fallará, pero no funcionará correctamente (de hecho, su funcionamiento dependerá del navegador):

// Atención, codigo erróneo a modo de ejemplo, NO USAR!
var Saludator = function(nom, enPlural) {
	this.nombre = nom
	this.saluda = function() {
		alert("hola "+quien(this.nombre))
	}
	// NO HACER ESTO NUNCA!!!!!!!
	if (enPlural) {
		function quien(n) { // Arggg!!
			return n + "s"
		}
	} else {
		function quien(n) { // Ouch!!
			return n
		}
	}
}
var obj1 = new Saludator("mundo", false)
var obj2 = new Saludator("mundo", true)
obj1.saluda() // "hola mundo"
obj2.saluda() // "hola mundo", pero es incorrecto!

En su lugar, definiendo los métodos con funciones como expresiones, si que funciona correctamente, ya que la creación del método no es más que una asignación que se produce dentro de una bifurcación condicional:

var Saludator = function(nom, enPlural) {
	this.nombre = nom
	this.saluda = function() {
		alert("hola "+this.quien())
	}
	if (enPlural) {
		this.quien = function() {
			return this.nombre + "s" // En plural
		}
	} else {
		this.quien = function() {
			return this.nombre  // En singular!!
		}
	}
}
var obj1 = new Saludator("mundo", false)
var obj2 = new Saludator("mundo", true)
obj1.saluda() // "hola mundo"
obj2.saluda() // "hola mundos", correto!

Métodos y atributos estáticos

Para crear un método estático tan solo lo tenemos que asignar a nuestra “clase” una propiedad más (recordemos que no es más que una función):

var Saludator = function(nom, enPlural) {
	this.nombre = nom
	this.enPlural = enPlural
	this.saluda = function() {
		alert("hola "+this.nombre)
	}
}
// Añadimos un método estático como atributo de la "clase" Saludator
Saludator.cloneSaludo = function(saludo) {
	return new Saludator("clonado "+saludo.nombre, saludo.enPlural)
}

var obj1 = new Saludator("mundo", false)
var obj2 = Saludator.cloneSaludo(obj1)
obj2.saluda() // "hola mundo"
obj2.saluda() // "hola clonado mundo"

Por supuesto, podemos añadir también atributos estáticos. Todas las propiedades estáticas (atributos o métodos) son siempre públicas y necesitan SIEMPRE el prefijo con el nombre de la “clase” delante siempre para acceder a ellas.

En este ejemplo, se usa la propiedad estática count de la clase Saludator para llevar la cuenta del número de objetos que se han creado:

var Saludator = function(nom) {
	// Se inicializa count a 0, si no existe
	Saludator.count = Saludator.count?Saludator.count:0
	Saludator.count ++
	this.nombre = nom
	this.saluda = function() {
		alert("hola "+this.nombre)
	}

}
Saludator.cloneSaludo = function(saludo) {
	return new Saludator("clonado "+saludo.nombre)
}

var obj1 = new Saludator("mundo", false)
var obj2 = Saludator.cloneSaludo(obj1)
obj2.saluda()

alert(Saludator.count) // "2", pues se han creado 2 objetos

Herencia

Como hemos dicho antes, la herencia basada en clases no existe en Javascript, sino que es herencia basada en prototipos. Esto significa que no hay una jerarquía de clases real (con su extends, acceso super, etc.), sino que cada “clase” tiene un objeto de referencia (como si fuera su clase padre) donde consultar atributos y métodos en caso de que nuestra clase no los encuentre. Podemos intentar simular una jerarquía (ojo, simular) usando estos objetos de referencia, llamados prototipos, con una propiedad especial que tienen todas las “clases” llamada prototype. Esta propiedad es asignable y su labor consiste en guardar un objeto. Veámoslo con un ejemplo:

var Saludator = function(nom) {
	this.nombre = nom
	this.saluda = function() { 
		alert("hola "+this.nombre) 
	}
}
// Añade un objeto al prototipo
Saludator.prototype = {
	apellido: "cruel",
	despide: function() { 
		alert("adios "+this.nombre+" "+this.apellido)
	}
}
var obj1 = new Saludator("mundo")
obj1.despide() // "adios mundo cruel"

Con esto lo que hacemos es asignar a la variable estática prototype en la clase Saludator un simple objeto con un atributo apellido y un método despide. Cuando invocamos a obj1.despide(), Javascript primero consulta en la propia clase Saludator y busca ese método; al no encontrarlo, consulta en el objeto prototype, donde si lo encuentra, por lo que lo ejecuta. Dentro de este método, se accede a this.nombre y this.apellido y sucede lo mismo: primero Javascript mira en la clase Saludator, donde solo encuentra el atributo nombre. Sin embargo, para encontrar el atributo apellido debe consultar en el objeto prototype.

Todas las clases tienen por defecto un objeto prototype vacío, como si al crearlas se hiciera prototype = {}. Por esta razón, podemos acceder a esta variable y asignarle los atributos de uno en uno. El siguiente ejemplo es completamente equivalente al anterior:

var Saludator = function(nom) {
	this.nombre = nom
}
// Añade nuevas propiedades al prototipo
Saludator.prototype.apellido = "cruel"
Saludator.prototype.despide = function() {
	alert("adios "+this.nombre+" "+this.apellido)
}

var obj1 = new Saludator("mundo")
obj1.despide() // "adios mundo cruel"

El problema de la herencia basada en prototipos es que la “clase padre” no es una clase, sino un objeto que se crea una única vez y que sirve de referencia para todas las instancias de la clase. Además, en una herencia basada en clases como la de Java, los constructores de las clases padre se heredan automáticamente (aunque no se invocan al instanciar nuevos objetos), pero en una de prototipos no. Así que para poder heredar e invocar constructores en Javascript tenemos que hacerlo a mano. Primero debemos crear una referencia al constructor de la clase padre y después llamarlo explícitamente:

var Saludator = function(nom) {
	this.nombre = nom
	this.saluda = function() { 
		alert("hola "+this.nombre) 
	}
}
var SubSaludator = function(nom, ape) {
	// Primero se añade una referencia al constructor padre,
	// y después se llama explícitamente.
	this.superConstructor = Saludator
	this.superConstructor(nom)
	
	this.apellido = ape
	this.despide = function() { 
		alert("adios "+this.nombre+" "+this.apellido)
	}
}
SubSaludator.prototype = new Saludator()

var obj1 = new SubSaludator("mundo", "cruel")
var obj2 = new SubSaludator("gente", "cruel")

obj1.saluda()  // "hola mundo"
obj2.saluda()  // "hola gente"
obj1.despide() // "adios mundo cruel"
obj2.despide() // "adios gente cruel"

Finalmente decir que podemos acceder al atributo prototype en todas clases, incluso en las clases del sistema, como String o Date. El siguiente código añade un nuevo método capitalize() a la clase de sistema String:

String.prototype.capitalize = function() {
	return this.charAt(0).toUpperCase() + this.substr(1).toLowerCase();
}

alert("hola".capitalize())

Namespaces

Una perlita para acabar. En algunos frameworks vemos como las “clases” tienen paquetes o espacio de nombres. Para empezar decir que ni son namespaces, ni packages ni nada, son solo objetos anidados unos dentro de otros. Y para acabar decir que es una muy buena practica que nosotros agrupemos nuestras funciones y objetos usando nuestro propio “espacio de nombres”.

Veámoslo con un ejemplo: es habitual tener varias funciones sueltas que nos ayuden (para manipular el Dom, validar un dato o lo que sea) junto con variables sueltas para apoyarnos así:

var dirty = false
function clickCancel() {
	var save = true
	if (dirty) {
		save = confirm("salvar antes de salir?")
	}
	if (save) clickSave()
}

function clickSave() {
	document.getElementById("myForm").submit()
}

Aquí tenemos una variable publica llamada dirty y dos métodos, también públicos, llamados clickCancel y clickSave. Pero sería mucho mejor usar nuestro propio objeto para encapsularlos:

var App = {
	dirty: false,
	clickCancel: function() {
		var save = true
		if (this.dirty) {
			save = confirm("salvar antes de salir?")
		}
		if (save) this.clickSave()
	},
	clickSave: function() {
		document.getElementById("myForm").submit()
	}
}

Ahora usaremos App.dirty, App.clickCancel() y App.clickSave(), consumiendo solo una variable pública en el ámbito global llamada App, y no tres como hacíamos antes. Si tenemos en cuenta que, generalmente, solemos crear decenas de funciones y variables públicas, está claro que la posibilidad de que usemos el mismo nombre de función o variable que ya exista en otro sitio disminuye considerablemente.

Y podemos anidar varios objetos, unos dentro de otros, hasta construir nuestro propio espacio de nombres:

var app = {util:{}}

app.util.forms = {
	dirty: false,
	clickCancel: function() {
		var save = true
		if (this.dirty) {
			save = confirm("salvar antes de salir?")
		}
		if (save) this.clickSave()
	},
	clickSave: function() {
		document.getElementById("myForm").submit()
	}
}

Ahora tenemos nuestro flamante espacio de nombres app.util.forms.dirty y podemos usar las funciones como app.util.forms.clickCancel() y app.util.forms.clickSave()

Y nada más por hoy. En el próximo post cerraremos la serie de Javascript hablando de scopes (ámbitos de visibilidad), hoisting y closures que es cuando la cosa se empieza a poner interesante de verdad. Y veremos también algunos ejemplos un poco más complejos, ¡hasta entonces!

Más información:

29 thoughts on “Profundizando en Javascript, parte 2: objetos, prototipos, herencia y namespaces

  1. Una pequeña queja: Realmente en elapartado que llamas Herencia, no hablas de herencia; y también, lo que dices de “prototype” está bastante flojo. ¿La principal ventaja de prototype es que podemos añadir a las clases del sistema? Meh.

    Lo digo todo de buen rollo y sin ánimo de molestar, eh, pero es que el título es **Profundizando** en Javascript.

  2. Respecto a libros… JavaScript tiene un porrón de años a sus espaldas y desde el día 1 a Netscape (el inventor de esta cosa) le interesó tener una buena documentación oficial del mismo, por lo que para conocer el lenguaje (otra cosa son usos creativos) siempre se ha podido recurrir a los “textos sagrados del JavaScript”, cuyo legado conserva ahora la Fundación Nozilla digooo Mozilla:

    https://developer.mozilla.org/en/JavaScript/Guide
    https://developer.mozilla.org/en/JavaScript/Reference

  3. Estupendo tutorial

    Una recomendación en el ejemplo:

    	var Saludator = function(nom) {
    	    this.saluda = function() {
    	       ...
    	    }
    
    	}
    

    Yo suelo poner:

    	    this.saluda = function saluda() {
    	       ...
    	    }
    

    O bien como:

    	    this.saluda = saluda;
    
                function saluda() {
    	       ...
    	    }
    

    Por dos razones:

    1) Queda como muy ordenadito y muy Java :)

    2) Los debuggers de JavaScript te muestran el nombre “saluda” en el “stack trace”, de otra manera la función es anónima pues “saluda” es una propiedad no el nombre de la función que llamas que es anónima y el stack trace te informa sobre las funciones por las que pasas no como las obtuviste. Este es un “problema” importante al depurar con el Internet Explorer.

    Por último, con un poco de esfuerzo se puede conseguir herencia y polimorfismo sin recurrir al lío de los protoype, constructor y demás hierbas alucinógenas (yo me mareo con ellos), la encapsulación es muy forzada y a mi me da bastante igual. Pero te lo dejo para tu siguiente artículo :)

  4. gnz/vnk: la verdad es que me he saltado hacer subclassing con SubClass.prototype = new SuperClass() y el constructor chaining con SubClass.protoype.constructor = SubClass, supongo que te refieres a eso, aunque también se puede hacer herencia copiando manualmente los miembros de las clases, como hacen los frameworks Prototypejs, jQuery o MooTools… creo que anoche tenía demasiadas ganas por publicarlo x’D Luego a la tarde añado las dos maneras de herencia, porque es bastante interesante

    jmarranz: el problema de hacerlo como tu dices, es que los “métodos” creados así son privados. Sin embargo, se puede arreglar haciendo:

    var Saludator = function(nom) {
    	this.nombre = nom
    	function saludaPrivado(nom) {
    		alert("hola "+nom)
    	}
    	this.saluda = function() { saludaPrivado(this.nombre) }
    }
    var obj1 = new Saludator("mundo")
    obj1.saluda();
    

    En este caso, aparece en el stacktrace y es pública también

  5. “los métodos creados así son privados”

    No te entiendo, la única diferencia es que el método tiene nombre y de la otra forma es anónimo (la propiedad a la que asignas es un puntero no la propia función)

    Yo hago herencia y polimorfismo sin prototype ni contructor, creo que lo aprendí hace un huevo de años de una librería llamada X-Objects de un tal YASD (Yanis no se que), ya no le encuentro a ese hombre en Internet al cual estoy “eternamente” agradecido :)

    function Saludador()
    {
       this.saludo = function saludo() { alert("Hola"); }
    }
    
    function SaludadorDerivado()
    {
        this.Saludador = Saludador;
        this.Saludador();
    
        this.Saludador_saludo = this.saludo;      
       this.saludo = function saludo() { this.Saludador_saludo(); alert("y adios"); }    
    }
    
    new SaludadorDerivado().saludo(); // Muestra dos alerts "Hola" "y adios"
    

    Seguro que hay formas mejores pero esta es la que más me gusta :)

  6. O WordPress es demasiado inteligente o no entiendo como …. funciona su sistema de comentarios, envío uno y no me lo muestra, ni necesita moderación ni nada, lo envío de nuevo y me dice que ya lo he enviado

  7. jmarranz, tus comentarios al tener código javascript no pasaron el filtro de wordpress, pero ya lo he arreglado e incluso maquetado :)

    Respecto a lo de metodos privados: me he confundido, no había visto que ya los haces públicos con this.saluda = saluda :)

    Respecto a tu forma de herencia es la misma que acabo de poner ahora en la nueva actualización, solo tiene una pequeña pega: al no asignar el prototype a una entidad de Saludador, no tienes polimorfismo

    alert(new SaludadorDerivado() instanceof Saludador) // false
    SaludadorDerivado.prototype = new Saludador
    alert(new SaludadorDerivado() instanceof Saludador) // true
    

    Pero, seamos sinceros, ¿a quien **** le importa en un lenguaje dinámico? x’D

    Gracias por tus comentarios Jose María. He añadido además un enlace a la documentación de Javascript de Mozilla. El ejemplo es genial porque muestra visualmente la jerarquía y pone el código equivalente en Java. Simplemente genial. :-)

  8. “al no asignar el prototype a una entidad de Saludador, no tienes polimorfismo”

    Lo de perder el polimorfismo no lo entiendo, no tienes el instanceof ciertamente si no “registras” tus “clases” en los prototype, pero lo demás es todo igual, sí tienes polimorfismo, depende de lo que llamemos polimorfismo, yo lo entiendo como la regla de que un método redefinido es siempre llamado el primero. En el ejemplo de antes el método “saludo” que se llama es el del más “derivado” y con el truco que añadí también puedes llamar al método de la “clase base”.

    Salvo el instanceof yo creo que no me pierdo algo más, el problema del “añadido” del prototype es que te obliga a diseñar tus “clases” pensando en el caso de tener que pasar parámetros “de pega” para el caso del objeto prototipo, en casos sencillos como en el que estamos no hay problema pero en modelos de “clases” más complicados es como pensar en un constructor por defecto pero sin tal constructor. A mi personalmente siempre me ha parecido algo obtuso lo de los prototype pero existe desde siempre y estará ahí para siempre.

  9. Nada de flojo, Alberto :)
    A mí no deja de sorprenderme en qué se ha convertido el uso de javascript desde el 96 hasta ahora… y la de potencia que “escondía” que ni imaginábamos cuando lo usábamos “a la antigua”. El día que ví por primera vez jQuery casi me echo a llorar ;)

    Gracias :) !!

  10. Jorge, sinceramente ¿en qué se ha convertido el uso del JavaScript desde el 96 hasta ahora?

    Yo lo único que veo es que ahora va MUCHO más rápido, que los navegadores son mucho más decentes ahora, incluso el MSIE 6 respecto a las versiones anteriores, que ahora los diseñadores web se atreven a hacer cosas más recargadas, bonitas y sofisticadas y que jQuery y primos hacen las cosas más sencillas, pero no veo nada fundamental desde el punto de vista técnico que haya cambiado desde entonces salvo que hablemos de AJAX y HTML 5.

    La pseudo-orientación a objetos de JavaScript y el DHTML llevan inventados más años que la tos, desde los tiempos de la basura del Navigator 4 y el MSIE 4 y jQuery NO es en absoluto la primera API JavaScript cross-platform, si yo mismo hice una hace 10 años (XPDOM) y de ahí me viene mi relativa fobia al JavaScript.

  11. Bueno la verdad es que javascript ha evolucionado, ya hay varias versiones con algunos cambios importantes aunque no afecten al nucleo del lenguaje ( http://en.wikipedia.org/wiki/JavaScript#Versions) aunque ahora mismo se use la 1.5 que es del 2000.
    Luego esta el tema de que durante muchos años se ha tratado al javascript como un lenguaje menor, un copia mala de java y programadores (muchos provenientes del mismo java) que no conocian el lenguaje minimamente han escrito cientos de miles de lineas infumables dandole muy mala fama al lenguaje.
    No es perfecto (el tener un parte buena supone que tb hay una mala) como tampoco java lo es pero esta claro que si ha triunfado es que se ajusta a lo que el desarrollo de la parte cliente necesita. Programando en el idiomaticamente puedes hacer codigo tan bueno o mejor que en java. O sea usando closures y funciones de “alto orden”, evaluacion parcial, callbacks, la cadena de prototipado, el tipado dinamico… ¿Por que buscar la herencia de java cuando tienes otras caracteristicas de lenguaje para hacer codigo reutilizable? Ese fue justo el error de los 90 con javascript, intentar programar en un lenguaje con la mentalidad de otro totalmente diferente.

  12. jneira no estoy de acuerdo contigo, el problema de JavaScript es que la gente se cansó de luchar contra los miles de problemas de compatibilidad que tenían los navegadores, no era un problema del propio lenguaje, el “core” de JavaScript ha sido siempre bastante respetado incluso por MSIE, el problema era el DOM y la manipulación de los CSS, esto es lo que escribía en torno al 2002 en la web de XPDOM:

    “old browser propietary technologies avoid leverage and standardize Web programming”

    Hartos de pelear “contra” los navegadores surgió la tendencia de delegar en el servidor cualquier operación aunque fuera menor en detrimento de JavaScript.

    “Ese fue justo el error de los 90 con javascript, intentar programar en un lenguaje con la mentalidad de otro totalmente diferente”

    Ese “error” yo creo que nunca ha ocurrido de forma generalizada, tengo serias dudas de que antes e incluso ahora la gente hiciera POO tal y como Vilches ha expuesto en este artículo, más bien lo contrario, JavaScript si no eres disciplinado favorece claramente el caos y el código espagetti, pero es que la presión por no generar mucho código JavaScript también invitaba a ello, invitaba a estructurar lo menos posible (para evitar el tiempo de transporte de mucho código y el tiempo de inicialización). Afortunadamente ahora la presión es mucho menor.

    Mi “relativa fobia” con JavaScript tiene más que ver con los serios problemas que tiene gestionar grandes cantidades de código JavaScript, hasta que no pasas por ahí y no se sufre no eres consciente de ello, por mucha OOP que hagas (que sin duda mitiga mucho los problemas).

    Respecto a patrones de programación, mi experiencia es que el 90% del código puede hacerse “tipo Java” y hay un 10% en el que los aspectos más dinámicos (inyección de un método o propiedad en un objeto concreto pero no en los demás del mismo “tipo”) pueden ser muy útiles, pero cuanto menos mejor porque hace el código menos predecible, recuerdo que alguien me comentaba este problema con Ruby cuando se quejaba en plan “quien narices ha inyectado este método en este objeto que siempre ha hecho tal cosa y ahora hace otra decidiendose en otro punto del código diferente al de su creación”.

    • Bueno yo he tenido que sufrir codigo en javascript hecho por programadores mediocres en java pero realmente pesimos en javascript. Un gran programador en java puede hacer buen codigo en javascript pero creo que nunca el mejor posible si no “abraza” la filosofia del lenguaje y aprovecha todas sus potencialidades (evitando sus debilidades). Bueno en realidad se puede aplicar a cualquier contexto de uso de varios lenguajes. Sinceramente creo que parte de la mala fama no justificada viene por ahi.
      Totalmente de acuerdo que la mayor parte del problema estuvo (y aun colea algo) en el api del dom y el intento de boicoteo de ms del javascript, la guerra de los navegadores y los problemas de incompatibilidad entre ellos (ahora son comitters de jquery ya ves tu)
      Se pueden usar las caracteristicas oop de javascript, que son un tanto diferentes a las de java (tomadas de http://en.wikipedia.org/wiki/Self_%28programming_language%29 ) y otras como el duck typing. El dinamismo del lenguaje lo hace mas expresivo y facilmente modificable con lo que responde bien a los cambios continuos del desarrollo web, a costa de tener que ser mas disciplinado (p.e. ya esta claro que hay que evitar hacerle monkey patching a las clases comunes del lenguaje e incluso a las tuyas propias) y/o hacer tests unitarios.
      En java he visto codigo spaghetti infumable sin usar oop o usandola y la oop no es la unica (algunos dirian que ni siquiera la mejor) forma de hacer codigo estructurado, reutilizable y expresivo. Javascript tiene caracteristicas del paradigma funcional que java esta empezando a adoptar ahora y que lo haceb mas *potente* ¿Por ejemplo se podria hacer una libreria con todas las funcionalidades y el encadenamiento de metodos de jquery en java en tan pocas lineas de codigo?
      Pero bueno ya me estoy metiendo en filosofadas que entran en el terreno de lo subjetivo o en el contexto en el que cada uno programe asi que me retiro.

  13. En *2001* Crockford ya escribía sobre The World’s Most Misunderstood Language y otros artículos sobre OOP en Javascript. Y lo hizo como respuesta a 5 años de gente que no se había dado cuenta de que Javascript no tenía nada que ver con Java (en los casos más extremos) o (en casos menos graves) que no había entendido que si la primera implementación de Javascript estaba escrita en CL ( http://mxr.mozilla.org/mozilla/source/js2/semantics/ ) no era por casualidad.

    Yo estoy con jneira (y con Crockford) en que una gran parte del problema de Javascript es la cantidad de gente intentando moldearlo a golpes para que se ajustara a sus ideas preconcebidas.

    Los problemas de incompatibilidades? Sí, por supuesto que han estado ahí y han hecho muchísimo daño, pero no tanto como lo otro. Quizá, fíjate, hiciera más daño que eso el simple problema genérico de que en el 2000, bajarse una página de 500Kb sí era un problema grave y por eso se optaba por tener más procesamiento en el servidor y minimizar el tráfico.

    En cuanto al tema del orden, a lo mejor el problema es empeñarse en que orden u organización implican necesariamente OOP.

  14. Por cierto! Una recomendación: En http://howtonode.org/ hablan principalmente de NodeJS pero también tienen tres artículos sobre “Learning Javascript with Object Graphs” que están bastante bien explicados.

  15. “En cuanto al tema del orden, a lo mejor el problema es empeñarse en que orden u organización implican necesariamente OOP.”

    Implica necesariamente OOP, AOP y programación funcional, estos dos últimos mucho más sencillos sin duda en JavaScript que en Java, o si no ¿como?

    ¿De verdad creeis que el problema de JavaScript ha sido que la gente “en general” hace mucho OOP en JavaScript? Yo no me lo creo.

    • Yo no diria eso, pero si solo se usa oop (o casi) y se solo usa al estilo de java estas limitando las posibilidades que tiemes de hacer mejor codigo y creo que en este caso lo que puedes ganar con esa limitacion (mas seguridad o confianza en el codigo?) no compensa lo que te pierdes

  16. “¿De verdad creeis que el problema de JavaScript ha sido que la gente “en general” hace mucho OOP en JavaScript?”

    Yo no he dicho eso, ojo. Sobre todo lo señalo en referencia a “mucho OOP”. Lo que he dicho es que creo que *la mayor parte* del problema es que la gente en general ha *intentado hacer las cosas en Javascript igual que las hacían en otros lenguajes* sin molestarse en entender cómo funcionaba realmente. No he limitado que eso signifique sólo “mucho OOP”.

  17. Yo creo que el problema de Javascript ha ido siempre que nadie se ha molestado nunca en llegar a dominarlo. Gracias a que sintaxis es “parecida” a la de Java (llaves, bucles, condiciones, etc), cuando se ha tenido que resolver algún problema puntual siempre se ha tirado de buscar en internet y copy-paste. Recuerdo que lo primero que hice en Javascript fue validar un formulario. No tuve que mirarme ningún tutorial, lo busqué en internet (y cuando digo internet, digo http://javascript.internet.com y http://www.dynamicdrive.com/) y lo encontré. Ahí descubrí el Javascript dinámico, dynduo (recuerdo que había una agencia de publicidad llamda doubleyou que hacía auténticas virguerías con esta librería) y muchos “trucos”, ahora horribles, con javascripts kilométricos para hacer menus desplegables, textos con scroll y cosas similares a base de eval y generar html en el dom con texto y innerHTML, sin apenas usar CSS. Estuve mucho, mucho tiempo sin mirar ningún tutorial de Javascript, porque todo se podía resolver haciendo pequeñas funciones a base de copiar de aquí y de allá. Y estoy con jmarranz en que el principal problema de hacer nada un poco más especial con Javascript es que si te funcionaba en Netscape no te funciona en IE.
    Pero desde hace bastante tiempo las cosas cambiaron, aunque no demasiado: ahora la gente lo que se lee son libros de jQuery, pero por lo general el código Javascript que se hace en los proyectos deja mucho que desear, y es porque nadie se interesa en aprenderlo porque no merece la pena, pero no es mediocre porque se mal use el OOP. De hecho, no he visto nunca usar Javascript con OOP en ningún proyecto! Ese es el problema de Javascript desde mi punto de vista.
    Lo bueno es que ahora hay motores más rápidos, navegadores más standard y Javascript parece que es un lenguaje que puede “salir” del navegador y usarse en más sitios: como funciones MapReduce en MongoDB o CocuchDB, como código de servidor en nodejs o como scripts de sistema con el motor V8. Por eso creo que ahora hay mejor código de calidad y más información en general que antes, donde reinaba el caos.

  18. @Vilches “no he visto nunca usar Javascript con OOP en ningún proyecto”

    Vaya parece que Vilches corrobora mi tesis :)

    Gonzalo yo no recuerdo los comentarios de Crockford, aunque algo he encontrado aquí:

    http://javascript.crockford.com/javascript.html

    pero desde luego no recuerdo que desde los padres de JavaScript se criticaran las técnicas vistas aquí mismo, más bien lo contrario, la gente de Netscape defendía JavaScript en el sentido de que era la versión dinámica de Java, es decir en vez de seguir una “ruta” declarativa estática como hace Java (es decir clases) JavaScript hace lo mismo y más de forma dinámica (construcción dinámica de cada objeto y prototipos), es decir ellos no decían “no hagas herencia en JavaScript que JavaScript no es para eso”, más bien decían que “la herencia en JavaScript se construye de forma diferente a Java” para defenderse de las críticas a JavaScript de que “no es un lenguaje orientado a objetos” ellos decían “JavaScript es orientado a prototipos que es la versión dinámica de la OOP”, los literales son mios e inventados.

    “Yo no diria eso, pero si solo se usa oop (o casi) y se solo usa al estilo de java estas limitando las posibilidades que tiemes de hacer mejor codigo”

    Yo se que tú eres un hombre muy funcional por lo menos en tus ratos libres :)

    Programación funcional haces sí o sí en JavaScript, para empezar porque el sistema de eventos del DOM está basado en listeners que son funciones, lo cual se refleja también en cualquier API JavaScript que se pone encima. Pero si te refieres a la bondad de la programación fluida de closures dentro de closures dentro de closures en plan:

    http://icant.co.uk/sandbox/jquerycodeview/ (jqueryCodeView.js)

    Pues hombre para unas pocas operaciones vale pero a medida que crece el asunto no deja de ser una nueva glorificación del código spagetti y te encuentras con trozos de código con montones de subniveles (closures/funciones anidadas).

    ¿Por qué? ¿no equivalen las anidaciones normalmente a bucles?

    Por eso mismo, porque en otros ámbitos cuatro for anidados empiezan a oler mal e invitan a que cada nivel de bucle lo metas en una función a la cual le das un nombre dándole así un significado semántico que aumenta la claridad del código, lo que en closures significaría empezar a dar nombres a las funciones en vez de ser anónimas, dichas funciones darán un valor semántico a lo que haces y te estructuran algo mejor el código evitando el mareo de llaves, y si resulta que varias funciones están conceptualmente relacionadas porque responden a diferentes acciones que se realizan en torno a un mismo dato/conjunto de datos pues resulta que “a lo mejor” quedarían mejor agrupadas como un “objeto” y quizás el paso de ese objeto quedaría mejor conceptualmente que el paso de las funciones sueltas, el problema es que “el bicho”, el objeto, entra de nuevo en escena como el aguafiestas que acaba fastidiando de nuevo el “paraíso funcional” pues acto seguido invita pensar en esas cosas raras como son la herencia y el polimorfismo :)

    • 1.- Con js puedes hacer oop totalmente de acuerdo, pero es una opcion mas disponible en el lenguaje. Estamos de acuerdo en que ademas tiene caracteristicas propias diferentes de java pero si quieres puedes imitarlo (lo contrario no es posible). Sin embargo creo que es mejor aprender y explotar las peculiaridades de la oop de js,por ejemplo:
      function (domething) { domething.cuack() } ¿¿domething tiene que pertenecer a una clase, extender un prototipo?? no, solo tener una funcion asociada (o sea un metodo) que se llame cuack. Que puedes mandar un dragon que cuando hace quack te destroce el programa, pues si. ¿Pero como puede ser mas potente, generico y flexible el polimorfismo? Que quieres ponerle un instanceof (o cualquier otra condicion) en tu programa para limitar quien puede hacer quack, pues la pones, ¿que no? pues no la pones. Be water my friend

      2.-¿Que es mejor hacer oop aunque no sea idiomatica en js que churrocode? ¿Que el codigo realmente malo en js no usa siquiera oop **o la usa mal**? Totalmente de acuerdo, pero como he dicho antes tambien en java puedes hacer churros para eso si que te da libertad (public static void hasmeUnChurroQueNoVeasDe500LineasCon5NivelesDeAnidamientoDeIFAndFors) Tambien puedes hacer *buena* programacion procedural (ver el framewrok play). Lo que no puedes hacer o es bastante complicado es programacion funcional.

      3.- La programacion funcional como bien sabes no se limita al uso de closures o funciones anonimas, cuyo abuso es tan malo como el abusar de los bucles en la imperativa como dices (mm en javascript puedes abusar de ambos de hecho). Y gracias a javascript no solo la puedo poner en practica en mis ratos libres. Oh wait que me pagan por hacer lo mismo que en mis ratos libres.. ¿es grave, doctor? :-P En js la practicas aunque no quieras como bien dices pero puedes arrinconarla o puedes abrazar el hecho de que javascript *tambien* es un lenguaje funcional.

  19. “los literales son mios e inventados.”

    JoséMaría, ante eso, ¿qué puedo decir? Yo no creo que las cosas sean como dices (sobre todo cuando admites que es una visión inventada por ti) pero tampoco tengo interés en discutir así. Tú tienes tu particular visión del asunto. Me parece bien. Pero eso, tú tienes tu visión, yo tengo la mía. No me interesa discutir más.

    Y digo todo esto de buen rollo y sin ningún tipo de molestia ni nada. De hecho, si lo que digo es síntoma de algo, es sólo de mi falta de interés y energía. Espero que nadie se lo tome mal.

    • Eso sí, me permito añadir como anécdota irrelevante que no pretende confirmar ni refutar ninguna tesis, que he visto más de 1 y más de 2 proyectos con *mucho* Javascript y con OOP en diferentes estilos. No todos, claro, pero sí un buen puñado.

  20. “JoséMaría, ante eso, ¿qué puedo decir?”

    Gonzalo, durante unos años seguí mozillazine.org, la web que informaba sobre el progreso del futuro Netscape 5.0 (que luego realmente no hubo 5.0 sino 6.0) como si fuera el Newyork Times, no tengo las citas literales pero si tengo el recuerdo vago de la gente de JavaScript de Netscape defendiendo que con JavaScript podías hacer las mismas cosas que Java pero de forma mucho más dinámica, es decir de forma *diferente* sin usar el concepto de clase, artículos sobre como hacer herencia con JavaScript ha habido miles y tú lo sabes, el ejemplo que puse más arriba lo aprendí por lo menos hace 10 años. No tengo el recuerdo de que la gente de Netscape menospreciara la POO lo cual sería absurdo pues con JavaScript puedes hacer POO incluso con herencia múltiple, AOP y programación funcional, y a estas alturas de la película menospreciar la OOP es terriblemente absurdo.

    De hecho no se de donde viene ese complejo anti-POO, Crockford al que citas escribe algo así:

    “In its present form, it is now a complete object-oriented programming language. But many opinions of the language are based on its immature forms.”

    En el apartado “Object-Oriented” Crockford se dedica a desmontar la idea de que JS no es orientado a objetos:

    “It does not have class-oriented inheritance, but it does have prototype-oriented inheritance.”

    Tú mismo en debugmodeon has hecho artículos sobre POO con JavaScript por lo que no entiendo en qué consiste “tu diferente visión”.

    Que algunos piensen que todo lo que “huela a estructurar” consiste en “forzar el uso del lenguaje” pues vale, seguramente, yo uso el lenguaje de la forma que a mi me sirve, ahora bien si al final el debate realmente es sobre si “estructurar o no”, en el barco “yo no estructuro” NO ME SUBO. Yo pienso en objetos y estructuro en objetos, hago herencia, polimorfismo y a veces herencias múltiples o mixins o traits o aspectos o como se quieran llamar y JavaScript me permite pensar así, y aunque también tiras un poco por lo funcional, al final acabas asociando “inevitablemente” malditas propiedades de estado a los objetos función que al final acaban siendo objetos con una función.

    Se avecinan tiempos en donde mucha gente empuja hacia el modelo cliente rico en JavaScript (que es una forma más bonita de decir “fat client” pero en fin) por lo que más les vale que piensen en formas de “estructurar” su código, forzado, natural, COMO SEA, porque de otra manera lo van a pasar canutas cuando el código crezca y crezca y varias manos lo toquen :)

    • “Yo pienso en objetos y estructuro en objetos, hago herencia, polimorfismo y a veces herencias múltiples o mixins o traits o aspectos o como se quieran llamar y **JavaScript me permite pensar así**”

      Nada mas que decir que efectivamente javascript es un gran lenguaje :-P

      Ojala java me permitiera programar como pienso.

  21. Respecto al tema del uso habitual de la OOP en JavaScript, una cosa que tiene el web es que tienes millones de ejemplos en los que curiosear si quieres y la verdad es que en las ocasiones en donde por lo que sea he tenido que ver código JavaScript… lo mejor es no recordarlo, mucho dinamismo estructural de JS pero al final yo lo que veo mayoritariamente es que se usa como si fuera C, por supuesto hay muchas excepciones, por ejemplo acabo de hechar un vistazo al JS de Twitter y FaceBook y como era de esperar no está mal (a pesar de ser un lío de narices pero ese es otro tema).

  22. Ala pero aun seguis por aqui dando mal??? :-P
    Creo que una conclusion de todo esto es que javascript tiene algo completamente diferente a java: te permite programar como en java si quieres cosa que no ocurre al contrario. Si quieres hacer codigo simplemente procedural, oop dinamica, programacion funcional o mezclarlas como mejor te apetezca con js puedes. Tambien te permite hacer codigo horrible. ¿Por que no hacer *buen* codigo procedural estilo c si consigues que sea reutilizable y comprensible y se ajusta al tamaño y objetivos de tu programa?
    Yo no menosprecio la oop pero esta claro que durante un tiempo se ha considerado el “fin de la historia” en la programacion, el paradigma definitivo para programar de forma correcta marginando otros paradigmas. No solo eso sino la oop estilo java que es muy diferente a uno de los primeros lenguajes que hizo oop: smalltalk (javascript tal vez se parezca mas a smalltalk que a java en eso y en ese sentido a vuelto a las raices de la oop). Afortunadamente eso ha empezado a cambiar. Ahora la bondad de un paradigma o el de usar varios mezclados (como en scala) depende del contexto en el que programas (con quien, para quien, para que lo haces) y no en axiomas e ideas preconcebidas.
    Context Oriented programming lo llamaria.
    Todo esto tiene un precio claro: si hay varias formas de hacer las cosas y libertad intelectual para elegir cual o mezclarlas las alternativas se multiplican y puedes encontrarte mas variedad de codigo que te puede costar mas asimilar si es muy diferente al que estas acostumbrado. La libertad y la diversidad siempre han tenido ese precio: libertad de hacer las cosas mal y tener que romper tus esquemas mentales pero para mi merecen la pena.

Comments are closed.