Hola Habr!
Hasta el día de hoy, solo he estado traduciendo artículos interesantes, en mi opinión, de autores de habla inglesa. Y ahora es el momento de escribir algo tú mismo. Para el primer artículo, elegí un tema que, estoy seguro, será útil para los desarrolladores junior que quieran crecer hasta el "medio", porque analizará la similitud / diferencia entre JavaScript y los lenguajes de programación clásicos (C ++, C #, Java) en términos de OOP. ¡Entonces comencemos!
Las disposiciones generales del paradigma.
Si observamos la definición de JavaScript en
Wikipedia , veremos el siguiente concepto:
JavaScript (/ ˈdʒɑːvɑːˌskrɪpt /; abbr. JS /ˈdʒeɪ.ɛs./) es un lenguaje de programación de paradigmas múltiples. Admite estilos orientados a objetos, imperativos y funcionales. Es una implementación del lenguaje ECMAScript (estándar ECMA-262).
Como se desprende de esta definición, JavaScript no existe por sí mismo, sino que es una implementación de algunas especificaciones de EcmaScript. Además, otros idiomas implementan esta especificación.
Los siguientes paradigmas están presentes en EcmaScript (en adelante ES):
- estructural
- OOP
- funcional
- imperativo
- orientado a aspectos (en casos raros)
OOP en ES se implementa en una
organización prototipo . De desarrolladores novatos en respuesta a la pregunta: "¿En qué se diferencia OOP en JS de OOP en lenguajes clásicos?" Como regla general, se vuelven muy vagos: "Las clases están en idiomas clásicos y los prototipos en JS".
En realidad, la situación es un poco más complicada. En términos de comportamiento, la diferencia entre la
Organización de clase dinámica y la
Organización prototipo es pequeña (ciertamente existe, pero no tan global).
Echa un vistazo a Python o Ruby. En estos idiomas, OOP se basa en una organización de clase dinámica. En ambos lenguajes, podemos cambiar dinámicamente la clase de un objeto a medida que el programa progresa y los cambios dentro de la clase también afectan dinámicamente las entidades que genera. Al igual que en JS, pero en JS, OOP se basa en prototipos.
Una diferencia significativa entre los idiomas con una organización de clase estática y una organización prototipo. La diferencia en sí misma es "hay clases". aquí los prototipos "no son tan significativos.
¿En qué se basa la Organización de clase estática?
La base de este tipo de OOP son los conceptos de "Clase" y "Esencia".
Una clase es un cierto conjunto formalizado de características generalizadas de entidades que puede generar. Es decir Este es un cierto plan general de todos los objetos generados por él.
Las características son de dos tipos. Propiedades (descripción de una entidad) y métodos (actividad de una entidad, su comportamiento).
Las entidades generadas por una clase son copias de esta clase, pero con propiedades inicializadas. Como vemos, la clase regula estrictamente la descripción de una entidad (que proporciona un conjunto de propiedades estrictamente definido) y su comportamiento (que proporciona una lista de métodos estrictamente definida).
Aquí hay un pequeño ejemplo en JAVA:
class Person{ String name;
Ahora cree una instancia de la clase:
public class Program{ public static void main(String[] args) { Person tom; } }
Nuestra entidad
tom tiene todas las características de la clase
Persona , también tiene todos los métodos de su clase.
El paradigma OOP proporciona una amplia gama de posibilidades para reutilizar código, una de estas características es la
herencia .
Una clase puede extender otra clase, creando así una relación de generalización-especialización. En este caso, las propiedades de la clase general (superclase) se copian a la esencia de la clase descendiente cuando se crean, y los métodos están disponibles por referencia (de acuerdo con la cadena jerárquica de herencia). En el caso de la tipificación de clase estática, esta cadena es
estática y, en el caso de la tipificación dinámica, puede cambiar durante la ejecución del programa. Esta es la diferencia más importante. Te aconsejo que recuerdes este momento. Además, cuando lleguemos a la organización Prototype, la esencia del problema de la respuesta "hay clases, luego prototipos" se hará evidente.
¿Cuáles son las desventajas de este enfoque?
Creo que es obvio que:
- En esencia, puede haber características que nunca serán útiles.
- Una clase no puede cambiar dinámicamente, agregar, eliminar propiedades y métodos que proporciona a las entidades generadas, es decir. No puede cambiar su firma.
- En esencia, las propiedades o métodos que no están presentes en la clase del padre (o cadena jerárquica de padres) no pueden estar presentes
- El consumo de memoria es proporcional al número de enlaces en la jerarquía de herencia (debido a las propiedades de copia)
¿En qué se basa la organización prototipo?
El concepto clave de la organización prototipo es un objeto dinámico mutable (dmo). DMO no necesita una clase. Él mismo puede almacenar todas sus propiedades y métodos.
Al establecer un DMO de una propiedad, verifica la presencia de esta propiedad en ella. Si hay una propiedad, simplemente se asigna; si no es así, la propiedad se agrega y se inicializa con el valor pasado. Los DMO pueden cambiar su firma durante el programa tantas veces como lo deseen.
Aquí hay un ejemplo:
Creo que todos en el tema saben que la sintaxis de clase ha aparecido en ES6, pero esto no es más que azúcar sintáctica, es decir prototipos debajo del capó. El código anterior no debe tomarse como una buena práctica de codificación. Esto no es más que una ilustración, se presenta de esta forma (ahora todas las personas normales usan clases de ES6) para no confundir al lector y enfatizar la diferencia en los conceptos teóricos.
Si enviamos el objeto Tom a la consola, veremos que el objeto en sí solo tiene el enlace _proto_, que siempre está presente de forma predeterminada. La referencia apunta al objeto Persona, que es un prototipo del objeto Tom.
Un prototipo es un objeto que sirve como prototipo para otros objetos o un objeto en el que otro objeto puede dibujar propiedades y métodos si son necesarios.
El prototipo de un objeto puede ser cualquier objeto, además, un objeto puede reasignar su prototipo durante el programa.
Volvamos a nuestro Tom:
Tom.name = 'Tom';
Tenga en cuenta que el nombre, las propiedades de edad y el método sayHi son las propiedades del objeto tomSon. Al mismo tiempo, en tomSon sayHi llamamos explícitamente al método prototipo sayHi como si estuviera en el objeto Tom, pero de hecho no está allí, y regresa implícitamente desde el prototipo Person. También operamos explícitamente en el nombre de propiedad del prototipo y obtenemos implícitamente la propiedad del apellido, que llamamos nuestra propiedad del objeto tomSon, pero en realidad no está allí. La propiedad del apellido se extrae implícitamente a través del enlace
__proto__ del prototipo.
Continuamos el desarrollo de la historia de nuestro Tom y su hijo John.
Tenga en cuenta que durante el programa cambiamos el prototipo del objeto ya creado. Esta es la similitud de la
organización prototipo y la
organización de clase dinámica . Es por eso que la respuesta "hay clases, hay prototipos" a la pregunta "¿cuál es la diferencia entre los lenguajes clásicos y JavaScript?" no es del todo correcto e indica algunos malentendidos de la teoría de OOP y su implementación en clases y / o prototipos.
Con la organización Prototype, a diferencia de la organización de clase Estática, tenemos la oportunidad de realizar cambios en el prototipo después de crear una entidad que herede las propiedades de este prototipo, y estos cambios afectarán a la entidad ya creada.
Ben.hobbies = ['chess', 'badminton'];
Esto se denomina
modelo de delegación de la organización prototipo o
herencia prototipo .
¿Cómo se determina la capacidad de una entidad para implementar cierto comportamiento?
En una organización de clase estática, esta operación implica verificar la pertenencia a la entidad en una clase particular que implementa el comportamiento requerido. En la organización prototipo, existe el concepto de
mecanografía . En el caso del tipeo de patos, verificar la capacidad de la entidad para implementar un comportamiento específico significará probar directamente la entidad para la capacidad de implementar este comportamiento en un momento determinado, es decir. en diferentes partes del programa, el resultado de la verificación puede ser diametralmente opuesto.
¿Cuáles son las ventajas de un enfoque prototipo?
- Mayor flexibilidad
- Las entidades no tienen propiedades que no necesitan.
¿Cuáles son las desventajas?
- Menos claro
- No siempre es fácil rastrear lo que sirvió como punto de partida para el comportamiento no deseado de la entidad, es decir, el prototipo es menos predecible que la organización de clase estática
- La comunidad de desarrollo de software no está lo suficientemente familiarizada con él, a pesar de la popularidad y prevalencia de JavaScript
Conclusión
En esto terminaremos hoy. Espero haber podido transmitir la idea de que la diferencia entre los lenguajes clásicos y JavaScript no está relacionada con la presencia / ausencia de clases y la presencia / ausencia de prototipos, sino más bien con la naturaleza estática / dinámica de la organización.
Por supuesto, mucho no se ha considerado. No me gustaría escribir artículos que sean demasiado largos, por lo que las características del modelo Cascade en la organización prototipo y las herramientas de OOP (Polimorfismo, Encapsulación, Abstracción, etc.) se analizarán en los artículos posteriores.