javascript - orientado - prototipo basado en la herencia basada en clase
propiedades javascript (3)
Aquí hay alrededor de un centenar de cuestiones de terminología, en su mayoría construidas alrededor de alguien (no de usted) tratando de hacer que su idea suene como The Best.
Todos los lenguajes orientados a objetos deben poder tratar varios conceptos:
- encapsulación de datos junto con operaciones asociadas en los datos, conocidos como miembros de datos y funciones de miembros, o como datos y métodos, entre otras cosas.
- herencia, la capacidad de decir que estos objetos son exactamente como ese otro conjunto de objetos, EXCEPTO por estos cambios
- polimorfismo ("muchas formas") en el que un objeto decide por sí mismo qué métodos se van a ejecutar, de modo que puede depender del idioma para enrutar sus solicitudes correctamente.
Ahora, en cuanto a la comparación:
Lo primero es toda la pregunta "clase" vs "prototipo". La idea comenzó originalmente en Simula, donde con un método basado en clases, cada clase representaba un conjunto de objetos que compartían el mismo espacio de estado (léase "valores posibles") y las mismas operaciones, formando así una clase de equivalencia. Si mira hacia atrás en Smalltalk, ya que puede abrir una clase y agregar métodos, esto es efectivamente lo mismo que lo que puede hacer en Javascript.
Más tarde, los lenguajes OO querían poder usar la verificación estática de tipos, por lo que obtuvimos la noción de un conjunto de clases fijo en tiempo de compilación. En la versión de clase abierta, tenía más flexibilidad; en la versión más nueva, usted tenía la capacidad de verificar algunos tipos de corrección en el compilador que de otro modo habría requerido una prueba.
En un lenguaje "basado en clases", esa copia ocurre en tiempo de compilación. En un lenguaje prototipo, las operaciones se almacenan en la estructura de datos prototipo, que se copia y modifica en tiempo de ejecución. Sin embargo, en abstracto, una clase sigue siendo la clase de equivalencia de todos los objetos que comparten el mismo espacio de estado y métodos. Cuando agrega un método al prototipo, efectivamente está haciendo un elemento de una nueva clase de equivalencia.
Ahora, ¿por qué hacer eso? principalmente porque crea un mecanismo simple, lógico y elegante en tiempo de ejecución. ahora, para crear un nuevo objeto o crear una nueva clase, simplemente tiene que realizar una copia profunda, copiando todos los datos y la estructura de datos del prototipo. Obtiene más o menos gratis herencia y polimorfismo: la búsqueda de métodos siempre consiste en pedir un diccionario para una implementación de método por nombre.
El motivo que terminó en JavaScript / ECMA script es básicamente que cuando comenzamos con esto hace 10 años, estábamos lidiando con computadoras mucho menos poderosas y navegadores mucho menos sofisticados. Elegir el método basado en prototipos significaba que el intérprete podía ser muy simple al tiempo que conservaba las propiedades deseables de la orientación del objeto.
En JavaScript, cada objeto es al mismo tiempo una instancia y una clase. Para hacer herencia, puede usar cualquier instancia de objeto como prototipo.
En Python, C ++, etc. hay clases e instancias como conceptos separados. Para hacer herencia, debes usar la clase base para crear una nueva clase, que luego se puede usar para producir instancias derivadas.
¿Por qué JavaScript fue en esta dirección (orientación de objetos basada en prototipos)? ¿Cuáles son las ventajas (y desventajas) del OO basado en prototipos con respecto al OO tradicional basado en la clase?
Debería echarle un vistazo a un gran libro en JavaScript de Douglas Crockford . Proporciona una muy buena explicación de algunas de las decisiones de diseño tomadas por los creadores de JavaScript.
Uno de los aspectos importantes del diseño de JavaScript es su sistema de herencia prototípico. Los objetos son ciudadanos de primera clase en JavaScript, tanto que las funciones regulares también se implementan como objetos (el objeto ''Function'' para ser precisos). En mi opinión, cuando fue diseñado originalmente para ejecutarse dentro de un navegador, estaba destinado a crear muchos objetos únicos. En el navegador DOM, encuentras esa ventana, documento, etc. todos los objetos singleton. También JavaScript es un lenguaje dinámico tipeado libremente (en contraposición a decir Python que es fuertemente tipado, lenguaje dinámico), como resultado se implementó un concepto de extensión de objeto mediante el uso de la propiedad ''prototipo''.
Así que creo que hay algunas ventajas para OO basado en protytype como se implementa en JavaScript:
- Adecuado en entornos sin tipeo, no es necesario definir tipos explícitos.
- Hace que sea increíblemente fácil implementar un patrón singleton (compare JavaScript y Java en este sentido, y sabrá de lo que estoy hablando).
- Proporciona formas de aplicar un método de un objeto en el contexto de un objeto diferente, agregando y reemplazando métodos dinámicamente de un objeto, etc. (cosas que no son posibles en un lenguaje fuertemente tipado).
Éstos son algunos de los contras de OO prototípico:
- No es una forma fácil de implementar variables privadas. Es posible implementar vars privados usando la magia de Crockford utilizando closures , pero definitivamente no es tan trivial como usar variables privadas en Java o C #.
- Aún no sé cómo implementar la herencia múltiple (por lo que vale) en JavaScript.
Una comparación, que es ligeramente sesgada hacia el enfoque basado en prototipos, se puede encontrar en el documento - Self: The power of simplicity . El documento hace los siguientes argumentos a favor de los prototipos:
Creación copiando La creación de objetos nuevos a partir de prototipos se logra mediante una operación simple, copiando, con una metáfora biológica simple, clonación. La creación de objetos nuevos a partir de las clases se logra mediante instanciación, que incluye la interpretación de información de formato en una clase. La instanciación es similar a construir una casa a partir de un plan. Copiar nos atrae como una metáfora más simple que la creación de instancias.
Ejemplos de módulos preexistentes . Los prototipos son más concretos que las clases porque son ejemplos de objetos en lugar de descripciones de formato e inicialización. Estos ejemplos pueden ayudar a los usuarios a reutilizar módulos haciéndolos más fáciles de entender. Un sistema basado en prototipos permite al usuario examinar a un representante típico en lugar de exigirle que le dé sentido a su descripción.
Soporte para objetos únicos . Self proporciona un marco que puede incluir fácilmente objetos únicos con su propio comportamiento. Dado que cada objeto tiene ranuras nombradas y las ranuras pueden contener estado o comportamiento, cualquier objeto puede tener ranuras o comportamiento únicos. Los sistemas basados en clases están diseñados para situaciones en las que hay muchos objetos con el mismo comportamiento. No hay soporte lingüístico para que un objeto posea su propio comportamiento único, y es incómodo ( piense en el patrón Singleton ) para crear una clase que garantice tener solo una instancia. El yo no padece ninguna de estas desventajas. Cualquier objeto se puede personalizar con su propio comportamiento. Un objeto único puede mantener el comportamiento único, y una "instancia" separada no es necesaria.
Eliminación de meta-regresión . Ningún objeto en un sistema basado en clases puede ser autosuficiente; otro objeto (su clase) es necesario para expresar su estructura y comportamiento. Esto lleva a una meta-regresión conceptualmente infinita: un point
es una instancia de la clase Point
, que es una instancia de metaclass Point
, que es una instancia de metametaclass Point
, ad infinitum. Por otro lado, en los sistemas basados en prototipos, un objeto puede incluir su propio comportamiento; no se necesita ningún otro objeto para insuflarle vida. Los prototipos eliminan metaregresión.
Self es probablemente el primer idioma para implementar prototipos. (También fue pionera en otras tecnologías interesantes como JIT, que más tarde llegó a la JVM. Por lo tanto, leer los otros documentos de Self también debería ser instructivo).