tutorial que prototipo objetos clases javascript inheritance prototype-programming

que - prototype javascript tutorial



Las desventajas de la herencia del prototipo de JavaScript, ¿qué son? (5)

Hace poco vi las presentaciones de JavaScript de Douglas Crockford , donde elogia la herencia del prototipo de JavaScript como si fuera lo mejor desde el pan blanco rebanado. Teniendo en cuenta la reputación de Crockford, bien podría serlo.

¿Puede alguien decirme cuál es la desventaja de la herencia del prototipo de JavaScript? (en comparación con la herencia de clase en C # o Java, por ejemplo)


Cosas que extraño al subclasificar un objeto existente en Javascript vs. heredar de una clase en C ++:

  1. No hay una forma estándar (incorporada en el lenguaje) de escribirlo que se vea igual sin importar qué desarrollador lo haya escrito.
  2. Escribir su código no produce naturalmente una definición de interfaz como lo hace el archivo de encabezado de clase en C ++.
  3. No hay una forma estándar de hacer variables o métodos de miembros protegidos y privados. Hay algunas convenciones para algunas cosas, pero nuevamente los diferentes desarrolladores lo hacen de manera diferente.
  4. No hay un paso de compilación para decirte cuándo has cometido errores tontos de tipeo en tu definición.
  5. No hay seguridad de tipo cuando lo quieres.

No me malinterpreten, hay un trillón de ventajas en la forma en que funciona la herencia del prototipo de javascript frente a C ++, pero estos son algunos de los lugares en los que creo que el javascript funciona menos fácilmente.

4 y 5 no están estrictamente relacionados con la herencia del prototipo, pero entran en juego cuando tienes un proyecto de tamaño significativo con muchos módulos, muchas clases y muchos archivos y deseas refactorizar algunas clases. En C ++, puede cambiar las clases, cambiar el número de personas que llama que pueda encontrar y luego dejar que el compilador encuentre todas las referencias restantes que necesita corregir. Si ha agregado parámetros, ha cambiado tipos, ha cambiado nombres de métodos, ha movido métodos, etc. ... el compilador le mostrará si necesita arreglar las cosas.

En Javascript, no hay una manera fácil de descubrir todas las piezas de código posibles que deben cambiarse sin ejecutar literalmente todas las rutas de código posibles para ver si se ha perdido algo o ha cometido algún error tipográfico. Si bien esta es una desventaja general de javascript, he encontrado que entra en juego particularmente cuando se refaccionan las clases existentes en un proyecto de tamaño significativo. Llegué cerca del final de un ciclo de lanzamiento en un proyecto JS de tamaño significativo y decidí que NO debería hacer ninguna refactorización para solucionar un problema (aunque esa era la mejor solución) porque el riesgo de no encontrar todas las ramificaciones posibles de ese cambio fue mucho mayor en JS que en C ++.

Por lo tanto, en consecuencia, me parece más arriesgado realizar algunos tipos de cambios relacionados con OO en un proyecto de JS.


Creo que el principal peligro es que varias partes pueden anular los métodos prototipo de los demás, lo que lleva a un comportamiento inesperado.

Esto es particularmente peligroso porque muchos programadores se entusiasman con el prototipo de "herencia" (lo llamaría extensión ) y, por lo tanto, comienzan a usarlo por todos lados, agregando métodos de izquierda y derecha que pueden tener un comportamiento ambiguo o subjetivo. En última instancia, si no se controla, este tipo de "proliferación de métodos prototipo" puede llevar a un código muy difícil de mantener.

Un ejemplo popular sería el método de trim . Podría ser implementado algo como esto por una de las partes:

String.prototype.trim = function() { // remove all '' '' characters from left & right }

Luego, otra parte podría crear una nueva definición, con una firma completamente diferente , tomando un argumento que especifique el carácter a recortar. De repente, todo el código que no pasa nada para trim no tiene ningún efecto.

O bien, otra parte vuelve a implementar el método para quitar '' '' caracteres '' '' y otras formas de espacio en blanco (por ejemplo, pestañas, saltos de línea). Esto podría pasar desapercibido por algún tiempo pero conducir a un comportamiento extraño en el futuro.

Dependiendo del proyecto, estos pueden considerarse peligros remotos. Pero pueden suceder, y desde mi comprensión, esta es la razón por la que bibliotecas como Underscore.js optan por mantener todos sus métodos dentro de los espacios de nombres en lugar de agregar métodos prototipo.

( Actualización : Obviamente, esta es una llamada de juicio.) Otras bibliotecas, a saber, el prototipo bien llamado, siguen la ruta del prototipo. No estoy tratando de decir que una de las formas es correcta o incorrecta, solo que este es el argumento He escuchado en contra del uso de métodos prototipo con demasiada liberalidad).


En mi experiencia, una desventaja significativa es que no se pueden imitar las variables miembro "privadas" de Java encapsulando una variable dentro de un cierre, pero aún así tener acceso a los métodos que se agregan posteriormente al prototipo.

es decir:

function MyObject() { var foo = 1; this.bar = 2; } MyObject.prototype.getFoo = function() { // can''t access "foo" here! } MyObject.prototype.getBar = function() { return this.bar; // OK! }

Esto confunde a los programadores OO a quienes se les enseña a hacer que las variables miembro sean privadas.


Extraño poder separar la interfaz de la implementación. En idiomas con un sistema de herencia que incluye conceptos como abstract o interface , podría, por ejemplo, declarar su interfaz en su capa de dominio pero colocar la implementación en su capa de infraestructura. (Cf. arquitectura de cebolla ). El sistema de herencia de JavaScript no tiene forma de hacer algo como esto.


Me gustaría saber si mi respuesta intuitiva coincide con lo que piensan los expertos.

Lo que me preocupa es que si tengo una función en C # (por el bien de la discusión) que toma un parámetro, cualquier desarrollador que escriba el código que llama a mi función sabe inmediatamente de la función qué tipo de parámetros toma y qué tipo de valor vuelve.

Con JavaScript "pato-tipando", alguien podría heredar uno de mis objetos y cambiar sus funciones y valores (Sí, sé que las funciones son valores en JavaScript) de casi cualquier forma imaginable para que el objeto que pasan a mi función tenga sin semejanza con el objeto, espero que pase mi función.

Siento que no hay una buena manera de que sea obvio cómo se supone que se debe llamar a una función.