Así que finalmente dejé de arrastrar mis pies todos estos años y decidí aprender JavaScript "correctamente". Uno de los elementos más deslumbrantes del diseño de idiomas es su implementación de herencia. Teniendo experiencia en Ruby, estaba realmente feliz de ver cierres y tipeos dinámicos; pero por mi vida no puedo entender qué beneficios se obtienen de las instancias de objetos que usan otras instancias por herencia.
271
Respuestas:
Sé que esta respuesta se retrasa 3 años, pero realmente creo que las respuestas actuales no proporcionan suficiente información sobre cómo la herencia prototípica es mejor que la herencia clásica .
Primero, veamos los argumentos más comunes que los programadores de JavaScript afirman en defensa de la herencia de prototipos (estoy tomando estos argumentos del conjunto actual de respuestas):
Ahora todos estos argumentos son válidos, pero nadie se ha molestado en explicar por qué. Es como decirle a un niño que estudiar matemáticas es importante. Claro que sí, pero al niño ciertamente no le importa; y no puedes hacer que un niño sea matemático diciendo que es importante.
Creo que el problema con la herencia de prototipos es que se explica desde la perspectiva de JavaScript. Me encanta JavaScript, pero la herencia de prototipos en JavaScript está mal. A diferencia de la herencia clásica, hay dos patrones de herencia prototípica:
Desafortunadamente, JavaScript utiliza el patrón constructor de la herencia prototípica. Esto se debe a que cuando se creó JavaScript, Brendan Eich (el creador de JS) quería que se pareciera a Java (que tiene herencia clásica):
Esto es malo porque cuando las personas usan constructores en JavaScript piensan en constructores que heredan de otros constructores. Esto está mal. En la herencia prototípica, los objetos heredan de otros objetos. Los constructores nunca entran en escena. Esto es lo que confunde a la mayoría de las personas.
Las personas de lenguajes como Java, que tiene herencia clásica, se confunden aún más porque, aunque los constructores parecen clases, no se comportan como clases. Como Douglas Crockford declaró:
Ahí tienes. Directo de la boca del caballo.
Verdadera herencia prototípica
La herencia de prototipos se trata de objetos. Los objetos heredan propiedades de otros objetos. Eso es todo al respecto. Hay dos formas de crear objetos utilizando la herencia de prototipos:
Nota: JavaScript ofrece dos formas de clonar un objeto: delegación y concatenación . De ahora en adelante, usaré la palabra "clonar" para referirme exclusivamente a la herencia por delegación, y la palabra "copiar" para referirme exclusivamente a la herencia por concatenación.
Basta de hablar. Veamos algunos ejemplos. Digamos que tengo un círculo de radio
5
:Podemos calcular el área y la circunferencia del círculo a partir de su radio:
Ahora quiero crear otro círculo de radio
10
. Una forma de hacer esto sería:Sin embargo, JavaScript proporciona una mejor manera: delegación . La
Object.create
función se usa para hacer esto:Eso es todo. Acabas de hacer una herencia prototípica en JavaScript. ¿No fue eso simple? Coges un objeto, lo clonas, cambias lo que necesites y, bueno, listo: tienes un objeto nuevo.
Ahora puede preguntar: "¿Cómo es esto simple? Cada vez que quiero crear un nuevo círculo necesito clonar
circle
y asignarle un radio manualmente". Bueno, la solución es utilizar una función para hacer el trabajo pesado por usted:De hecho, puede combinar todo esto en un único objeto literal de la siguiente manera:
Herencia de prototipos en JavaScript
Si observa en el programa anterior, la
create
función crea un cloncircle
, le asigna un nuevoradius
y luego lo devuelve. Esto es exactamente lo que hace un constructor en JavaScript:El patrón constructor en JavaScript es el patrón prototipo invertido. En lugar de crear un objeto, crea un constructor. La
new
palabra clave une elthis
puntero dentro del constructor a un clon delprototype
del constructor.¿Suena confuso? Es porque el patrón constructor en JavaScript complica innecesariamente las cosas. Esto es lo que la mayoría de los programadores encuentran difícil de entender.
En lugar de pensar en objetos que heredan de otros objetos, piensan en constructores que heredan de otros constructores y luego se confunden por completo.
Hay muchas otras razones por las que se debe evitar el patrón de constructor en JavaScript. Puedes leer sobre ellos en mi blog aquí: Constructores vs Prototipos
Entonces, ¿cuáles son los beneficios de la herencia prototípica sobre la herencia clásica? Veamos nuevamente los argumentos más comunes y expliquemos por qué .
1. La herencia prototípica es simple
CMS afirma en su respuesta:
Consideremos lo que acabamos de hacer. Creamos un objeto
circle
que tenía un radio de5
. Luego lo clonamos y le dimos al clon un radio de10
.Por lo tanto, solo necesitamos dos cosas para que la herencia de prototipos funcione:
Object.create
).En contraste, la herencia clásica es mucho más complicada. En herencia clásica tienes:
Tienes la idea. El punto es que la herencia prototípica es más fácil de entender, más fácil de implementar y más fácil de razonar.
Como Steve Yegge lo pone en su clásica publicación de blog " Retrato de un N00b ":
En el mismo sentido, las clases son solo metadatos. Las clases no son estrictamente necesarias para la herencia. Sin embargo, algunas personas (generalmente n00bs) encuentran que las clases son más cómodas para trabajar. Les da una falsa sensación de seguridad.
Como dije anteriormente, las clases dan a las personas una falsa sensación de seguridad. Por ejemplo, obtienes demasiados
NullPointerException
s en Java incluso cuando tu código es perfectamente legible. Creo que la herencia clásica generalmente se interpone en la programación, pero tal vez eso sea solo Java. Python tiene un sorprendente sistema clásico de herencia.2. La herencia prototípica es poderosa
La mayoría de los programadores que provienen de un contexto clásico argumentan que la herencia clásica es más poderosa que la herencia prototípica porque tiene:
Este reclamo es falso. Ya sabemos que JavaScript admite variables privadas a través de cierres , pero ¿qué pasa con la herencia múltiple? Los objetos en JavaScript solo tienen un prototipo.
La verdad es que la herencia de prototipos admite la herencia de múltiples prototipos. La herencia de prototipos simplemente significa que un objeto hereda de otro objeto. En realidad, hay dos formas de implementar la herencia prototípica :
Sí, JavaScript solo permite que los objetos deleguen en otro objeto. Sin embargo, le permite copiar las propiedades de un número arbitrario de objetos. Por ejemplo,
_.extend
hace exactamente esto.Por supuesto, muchos programadores no consideran que esto sea una verdadera herencia porque
instanceof
yisPrototypeOf
dicen lo contrario. Sin embargo, esto puede remediarse fácilmente almacenando una serie de prototipos en cada objeto que hereda de un prototipo mediante concatenación:Por lo tanto, la herencia prototípica es tan poderosa como la herencia clásica. De hecho, es mucho más poderoso que la herencia clásica porque en la herencia prototípica puede elegir qué propiedades copiar y qué propiedades omitir de diferentes prototipos.
En la herencia clásica es imposible (o al menos muy difícil) elegir qué propiedades desea heredar. Utilizan clases e interfaces virtuales para resolver el problema del diamante .
Sin embargo, en JavaScript es muy probable que nunca escuche sobre el problema del diamante porque puede controlar exactamente qué propiedades desea heredar y de qué prototipos.
3. La herencia prototípica es menos redundante
Este punto es un poco más difícil de explicar porque la herencia clásica no necesariamente conduce a un código más redundante. De hecho, la herencia, ya sea clásica o prototípica, se usa para reducir la redundancia en el código.
Un argumento podría ser que la mayoría de los lenguajes de programación con herencia clásica están tipados estáticamente y requieren que el usuario declare explícitamente los tipos (a diferencia de Haskell, que tiene tipeo estático implícito). Por lo tanto, esto conduce a un código más detallado.
Java es conocido por este comportamiento. Recuerdo claramente a Bob Nystrom mencionando la siguiente anécdota en su publicación de blog sobre Pratt Parsers :
Nuevamente, creo que eso es solo porque Java apesta mucho.
Un argumento válido es que no todos los idiomas que tienen herencia clásica admiten herencia múltiple. Nuevamente, Java me viene a la mente. Sí, Java tiene interfaces, pero eso no es suficiente. A veces realmente necesitas herencia múltiple.
Dado que la herencia prototípica permite la herencia múltiple, el código que requiere herencia múltiple es menos redundante si se escribe usando la herencia prototípica en lugar de en un lenguaje que tiene herencia clásica pero no herencia múltiple.
4. La herencia prototípica es dinámica
Una de las ventajas más importantes de la herencia de prototipos es que puede agregar nuevas propiedades a los prototipos después de su creación. Esto le permite agregar nuevos métodos a un prototipo que estarán disponibles automáticamente para todos los objetos que deleguen en ese prototipo.
Esto no es posible en la herencia clásica porque una vez que se crea una clase no se puede modificar en tiempo de ejecución. Esta es probablemente la mayor ventaja de la herencia prototípica sobre la herencia clásica, y debería haber estado en la parte superior. Sin embargo, me gusta guardar lo mejor para el final.
Conclusión
La herencia prototípica es importante. Es importante educar a los programadores de JavaScript sobre por qué abandonar el patrón constructor de la herencia prototípica en favor del patrón prototípico de la herencia prototípica.
Necesitamos comenzar a enseñar JavaScript correctamente y eso significa mostrar a los nuevos programadores cómo escribir código usando el patrón prototípico en lugar del patrón constructor.
No solo será más fácil explicar la herencia prototípica utilizando el patrón prototípico, sino que también será un mejor programador.
Si le gustó esta respuesta, también debería leer la publicación de mi blog sobre " Por qué es importante la herencia de prototipos ". Créame, no se decepcionará.
fuente
Object.create
está creando un nuevo objeto con el prototipo especificado. Su elección de palabras da la impresión de que el prototipo se clona.Permítame responder la pregunta en línea.
La herencia del prototipo tiene las siguientes virtudes:
Sin embargo, tiene las siguientes desventajas:
Creo que puede leer entre las líneas anteriores y encontrar las ventajas y desventajas correspondientes de los esquemas tradicionales de clase / objeto. Por supuesto, hay más en cada área, así que dejaré el resto a otras personas que respondan.
fuente
En mi opinión, el principal beneficio de la herencia de prototipos es su simplicidad.
La naturaleza prototípica del lenguaje puede confundir a las personas que son clásicamente formación , pero resulta que en realidad este es un concepto realmente simple y poderoso, la herencia diferencial .
No necesita hacer una clasificación , su código es más pequeño, menos redundante, los objetos heredan de otros objetos más generales.
Si piensas prototípicamente , pronto notará que no necesita clases ...
La herencia prototípica será mucho más popular en el futuro cercano, la especificación ECMAScript 5th Edition introdujo el
Object.create
método, que le permite producir una nueva instancia de objeto que hereda de otra de una manera muy simple:Esta nueva versión del estándar está siendo implementada por todos los proveedores de navegadores, y creo que comenzaremos a ver una herencia de prototipos más pura ...
fuente
Realmente no hay mucho para elegir entre los dos métodos. La idea básica a entender es que cuando se le da al motor de JavaScript la propiedad de un objeto para leer, primero verifica la instancia y, si falta esa propiedad, verifica la cadena del prototipo. Aquí hay un ejemplo que muestra la diferencia entre prototipado y clásico:
Prototipo
Clásico con métodos de instancia (ineficiente porque cada instancia almacena su propia propiedad)
Clásico eficiente
Como puede ver, dado que es posible manipular el prototipo de "clases" declaradas en el estilo clásico, no hay realmente ningún beneficio en el uso de la herencia prototípica. Es un subconjunto del método clásico.
fuente
Desarrollo web: herencia prototípica versus herencia clásica
http://chamnapchhorn.blogspot.com/2009/05/prototypal-inheritance-vs-classical.html
Herencia prototípica Vs clásica - code q & a resuelto
Herencia prototípica Vs clásica
fuente