JavaScript dispone de fuertes capacidades de programación orientada a objetos, a pesar de que han tenido lugar algunos debates respecto a las diferencias de sus capacidades en comparación con otros lenguajes.
La programación orientada a objetos es un paradigma de programación que utiliza la abstracción para crear modelos basados en el mundo real. Utiliza diversas técnicas de paradigmas previamente establecidas, incluyendo la modularidad, polimorfismo y encapsulamiento. Hoy en día, muchos lenguajes de programación (como Java, JavaScript, C#, C++, Python, PHP, Ruby y Objective-C) soportan programación orientada a objetos (POO).
La programación orientada a objetos puede considerarse como el diseño de software a través de un conjunto de objetos que cooperan, a diferencia de un punto de vista tradicional en el que un programa puede considerarse como un conjunto de funciones, o simplemente como una lista de instrucciones para la computadora. En la programación orientada a objetos, cada objeto es capaz de recibir mensajes, procesar datos y enviar mensajes a otros objetos. Cada objeto puede verse como una pequeña máquina independiente con un papel o responsabilidad definida.
POO pretende promover una mayor flexibilidad y facilidad de mantenimiento en la programación y es muy popular en la ingeniería de software a gran escala. Gracias a su fuerte énfasis en la modularidad, el código orientado a objetos está concebido para ser más fácil de desarrollar y más fácil de entender posteriormente, prestándose a un análisis más directo, a una mayor codificación y comprensión de situaciones y procedimientos complejos que otros métodos de programación menos modulares.
La programación basada en prototipos es un estilo de programación orientada a objetos en la que las clases no están presentes y la reutilización de comportamiento (conocido como herencia en lenguajes basados en clases) se lleva a cabo a través de un proceso de decoración de objetos existentes que sirven de prototipos. Este modelo también se conoce como programación sin clases, orientada a prototipos o basada en ejemplos.
El ejemplo original (y más canónico) de un lenguaje basado en prototipos es el lenguaje de programación Self desarrollado por David Ungar y Randall Smith. Sin embargo, el estilo de programación sin clases se ha hecho cada vez más popular y ha sido adoptado para lenguajes de programación como JavaScript y varios otros.
Un espacio de nombres es un objeto:
Vamos a crear un objeto global llamado MIAPLICACION
var MIAPLICACION = MIAPLICACION || {};
Nota: Para continuar con las mejores prácticas vamos a utilizar mayúsculas para los namespace.
En el código de ejemplo anterior comprobamos si MIAPLICACION ya se encuentra definida. Si es así utilizamos el objeto global MIAPLICACION que existe; si este no existe creamos un objeto vacío llamado MIAPLICACION que encapsulará métodos, funciones, variables y otros objetos que vayamos a crear.
También podemos crear Sub-espacios de nombres:
Nota: este y todos los demás ejemplos suponen que una función llamada alert (como el que se incluye en los navegadores web) se define de forma global. La función alert no es realmente parte de JavaScript.
Cada objeto en JavaScript es una instancia del objeto Object, por lo tanto, hereda todas sus propiedades y métodos.
La clase
JavaScript es un lenguaje basado en prototipos que no contiene ninguna declaración de clase, como se encuentra, por ejemplo, en C + + o Java. Esto es a veces confuso para los programadores acostumbrados a los lenguajes con una declaración de clase. En su lugar, JavaScript utiliza funciones como clases. Definir una clase es tan fácil como definir una función. En el ejemplo siguiente se define una nueva clase llamada Persona.
}
Las propiedades son variables contenidas en la clase, cada instancia del objeto tiene dichas propiedades. Las propiedades deben establecerse a la propiedad prototipo de la clase (función), para que la herencia funcione correctamente.
Para trabajar con propiedades dentro de la clase se utiliza la palabra reservada this , que se refiere al objeto actual. El acceso (lectura o escritura) a una propiedad desde fuera de la clase se hace con la sintaxis: NombreDeLaInstancia.Propiedad. Es la misma sintaxis utilizada por C++, Java y algunos lenguajes más. (Desde dentro de la clase la sintaxis es this.Propiedad que se utiliza para obtener o establecer el valor de la propiedad).
En el siguiente ejemplo definimos la propiedad primerNombre de la clase Persona y la definimos en la creación de la instancia.
Los métodos siguen la misma lógica que las propiedades, la diferencia es que son funciones y se definen como funciones. Llamar a un método es similar a acceder a una propiedad, pero se agrega () al final del nombre del método, posiblemente con argumentos.
En el siguiente ejemplo se define y utiliza el método diHola() para la clase Persona.
En JavaScript los métodos son objetos como lo es una función normal y se vinculan a un objeto como lo hace una propiedad, lo que significa que se pueden invocar desde "fuera de su contexto". Considera el siguiente código de ejemplo:
En el ejemplo se muestran todas las referencias que tenemos de la función diHola — una de ellas es persona1, otra en Persona.prototype, en la variable funcionSaludar, etc. — todas se refieren a la misma función.
El valor durante una llamada a la función depende de como realizamos esa llamada. En el común de los casos cuando la llamamos desde una expresión donde tenemos a la función desde la propiedad del objeto — persona1.diHola().— Se establece en el objeto que tenemos en la función (persona1), razón por la cual persona1.diHola() utiliza el nombre "Alicia" y persona2.diHola() utiliza el nombre "Sebastian". Pero si realizamos la llamada de otra manera, se establecerá de forma diferente: Llamándola desde una variable —funcionSaludar() — Este establece al objeto global (windows, en los navegadores). Desde este objeto (probablemente) no tiene a la propiedad primerNombre, por lo que finalizará con "Hola, Soy indefinido". (El cual se incluye en modo de código suelto, sino sería diferente [un error] en modo estricto, pero para evitar confusiones ahora no vamos a entra en detalles.) O podemos establecerla de forma explicita utilizando Function.call (ó Function.apply), como se muestra al final del ejemplo funcionSaludar.call(persona1).
HerenciaLa herencia es una manera de crear una clase como una versión especializada de una o más clases (JavaScript sólo permite herencia simple). La clase especializada comúnmente se llama hija o secundaria, y la otra clase se le llama padre o primaria. En JavaScript la herencia se logra mediante la asignación de una instancia de la clase primaria a la clase secundaria, y luego se hace la especialización.
En el siguiente ejemplo definimos la clase Estudiante como una clase secundaria de Persona . Luego redefinimos el método diHola() y agregamos el método diAdios().
Con respecto a la línea Estudiante.prototype = Object.create(Persona.prototype); : Sobre los motores antiguos de JavaScript sin Object.create, se puede utilizar un "polyfill" (aka "shim", vea el enlace del artículo), o se puede utilizar una función que obtiene el mismo resultado, como por ejemplo:
En el ejemplo anterior, Estudiante no tiene que saber cómo se aplica el método caminar() de la clase Persona, pero, sin embargo, puede utilizar ese método. La clase Estudiante no tiene que definir explícitamente ese método, a menos que queramos cambiarlo. Esto se denomina la encapsulación, por medio de la cual cada clase hereda los métodos de su elemento primario y sólo tiene que definir las cosas que desea cambiar.
AbstracciónUn mecanismo que permite modelar la parte actual del problema de trabajo. Esto se puede lograr por herencia (especialización) o por composición. JavaScript logra la especialización por herencia y por composición al permitir que las instancias de clases sean los valores de los atributos de otros objetos.
La clase Function de JavaScript hereda de la clase de Object (esto demuestra la especialización del modelo) y la propiedad Function.prototype es un ejemplo de Objeto (esto demuestra la composición)
Al igual que todos los métodos y propiedades están definidas dentro de la propiedad prototipo, las diferentes clases pueden definir métodos con el mismo nombre. Los métodos están en el ámbito de la clase en que están definidos. Esto sólo es verdadero cuando las dos clases no tienen una relación primario-secundario (cuando uno no hereda del otro en una cadena de herencia).
Desarrollaremos una clase que represente un cliente de un banco.
La clase Cliente tiene como atributos:
y las responsabilidades o métodos de la clase son:
Luego debemos implementar los siguientes métodos (normalmente el constructor se utiliza el caracter mayúscula):
El nombre de la clase coincide con el nombre de la función principal que implementamos (también llamado constructor de la clase):
A ésta función llegan como parámetro los valores con que queremos inicializar los atributos. Con la palabra clave 'this' diferenciamos los atributos de los parámetros (los atributos deben llevar la palabra clave this)
También en el constructor inicializamos la referencia a todos los métodos que contendrá la clase (esto es muy importante y necesario para entender porque las otras dos funciones pertenecen a esta clase):
Por último, implementamos todos los métodos de la clase:
De nuevo recordemos que diferenciamos los atributos de la clase por la palabra clave this.
Ahora veamos el archivo HTML completo donde además definiremos un objeto de la clase planteada:
Para definir un objeto de la clase Cliente tenemos:
Luego las llamadas a métodos le antecedemos el nombre del objeto llamado cliente1:
Podemos decir que la ventaja que podemos obtener con el planteo de clases es hacer nuestros programas mucho más organizados, entendibles y fundamentalmente poder reutilizar clases en distintos proyectos.