¿Cómo se usa la escritura de pato en javascript sin verificar siempre las propiedades y los métodos?

11

Sé que javascript utiliza la tipificación de pato y al principio pensé que esto facilitaría el polimorfismo en comparación con lenguajes fuertemente tipados como C #. Pero ahora mis funciones que toman argumentos están llenas de cosas como:

if(myObj.hasSomeProperty())

o

if(myObj.hasSomeMethod())

o

if(isNumber(myParam))

etc.

Esto es realmente feo para mí. Vengo de un fondo de C # y encuentro que las interfaces definidas son mucho mejores.

Me pregunto si intento aplicar incorrectamente estrategias que son efectivas en lenguajes estáticamente escritos y ¿hay alguna forma mejor de hacerlo en JavaScript?

Sé que no podría verificarlo, pero rastrear los errores de tiempo de ejecución de JavaScript puede ser una pesadilla, ya que no siempre ocurren donde el error realmente ocurre en el código.

Legión
fuente
2
Creo que podrías estar buscando la naturaleza de un lenguaje de tipo dinámico. Debe acostumbrarse a la mentalidad de que se producirán muchos errores en tiempo de ejecución en lugar de en tiempo de compilación. Si siente la necesidad de verificar si cada argumento es un número en cada función que ingresa números, puede convertirse en una carga (aunque tal vez valga la pena si está enviando una biblioteca con la seguridad como objetivo principal). Para cualquier cosa de escala, considero que es esencial permitir que las funciones fallen si se pasan los tipos incorrectos. En cambio, un enfoque más productivo podría estar en la construcción de pruebas.
Donde podría ayudar hacer estas comprobaciones para asegurarse de que los tipos se ajustan a los requisitos de interfaz necesarios (comprobar para ver si tienen los métodos requeridos, por ejemplo) está en sus funciones más centrales y ampliamente utilizadas (las que tienen inestabilidad = 0 con Eferente / aferente métrica de acoplamiento Martin proporciona). Ese debería ser un objetivo bastante pequeño. Por lo general, hay muchas funciones locales únicas que están aisladas en su alcance; probablemente no necesiten un conjunto tan completo de comprobaciones de tiempo de ejecución. No acumulan mucha complejidad.
Cambie a Escribir secuencia de comandos. Todavía está tipeado, pero admite tipeo estático para detectar muchos errores en tiempo de compilación.
CodesInChaos
2
Te has topado con el mayor problema del tipeo de patos: su poder deriva de su debilidad. Si desea hacer JavaScript orientado a objetos, solo tiene que vivir con los errores de tiempo de ejecución y esperar que las pruebas de su unidad los encuentren poco después de crearlos :-(
Ross Patterson
@RossPatterson El problema de los OP es con la escritura dinámica, no con la escritura de pato. TypeScript y Go tienen ambos tipos de pato, pero evitan el problema del OP. El problema con la escritura de los patos es diferente, es decir, puede tener miembros que pasan la prueba del pato pero no cumplen el contrato que espera.
CodesInChaos

Respuestas:

11

¿Cómo se usa la escritura de pato en javascript sin verificar siempre las propiedades y los métodos?

Simple: no siempre verifique las propiedades y los métodos.

En Ruby, lo que llamas se llama "mecanografía de pollo". En un lenguaje de tipo dinámico, simplemente confía en que la persona que llama le pasa un objeto adecuado. El trabajo de la persona que llama es honrar su parte del contrato.

Sé que javascript utiliza la tipificación de pato y al principio pensé que esto facilitaría el polimorfismo en comparación con lenguajes fuertemente tipados como C #.

Está confundiendo múltiples ejes ortogonales de escribir aquí. Hay cuatro ejes ortogonales de escritura:

  • Cuándo : escritura dinámica (los tipos no se conocen y se verifican hasta el tiempo de ejecución) frente a la escritura estática (los tipos se conocen y se verifican antes del tiempo de ejecución)
  • Qué : tipificación de pato (los tipos se basan en el comportamiento ), tipificación estructural (los tipos se basan en la estructura ) y tipificación nominal (los tipos se basan en el nombre )
  • ¿Puedes verlos? escritura explícita (los tipos tienen que ser anotados explícitamente) versus escritura implícita (los tipos se infieren)
  • escritura fuerte versus escritura débil: es posible que haya notado que no le di a este un título pegadizo ni una explicación entre paréntesis, porque a diferencia de los siete términos anteriores, que tienen una definición precisa única universalmente aceptada, estos dos términos tener alrededor de una docena de definiciones vagas semi-ampliamente utilizadas que se contradicen entre sí; idealmente, debe evitar estos términos por completo, y si debe usarlos, defínalos con precisión primero

Como mencionó C #: en su mayoría está tipado estáticamente, pero admite el tipeo dinámico a través del tipo dynamic, en su mayoría está tipeado nominalmente, pero los tipos anónimos usan tipeo estructural, y se puede argumentar que los patrones sintácticos (como la sintaxis de comprensión de consultas LINQ) son duck -tipo o estructuralmente tipado, en su mayoría está explícitamente tipado pero admite tipeo implícito para argumentos de tipo genérico y variables locales (aunque el caso de la variable local es bastante extraño en comparación con la mayoría de los otros idiomas, porque no puede dejar el tipo fuera, sino que debe darle un pseudotipo explícitovar, en otras palabras, si quieres un tipo implícito, tienes que decirlo explícitamente). Si C # tiene un tipo fuerte o débil, es una cuestión de qué definición de los dos términos que usa, sin embargo, tenga en cuenta que puede haber muchos errores de tipo de tiempo de ejecución en C #, especialmente debido a la covarianza de matriz insegura.

Sé que no podría verificarlo, pero rastrear los errores de tiempo de ejecución de JavaScript puede ser una pesadilla, ya que no siempre ocurren donde el error realmente ocurre en el código.

La depuración no es una habilidad fácil de aprender. Sin embargo, existen técnicas para facilitar la depuración, por ejemplo, Saff Squeeze es una técnica descrita por Kent Beck que utiliza pruebas y refactorización para la depuración:

Hit 'em High, Hit' em Low :

Pruebas de regresión y el apretón de Saff

Kent Beck, Instituto de los Tres Ríos

Resumen: Para aislar un defecto de manera efectiva, comience con una prueba a nivel del sistema y en línea y pode progresivamente hasta obtener la prueba más pequeña posible que demuestre el defecto.

Jörg W Mittag
fuente
El enlace hit em high hit em low obtiene un http 500 para mí, con "Página ya no está disponible" como mensaje orientado al ser humano.
joshp
El dominio threeriversinstitute.org parece haber sido abandonado.
Bart van Ingen Schenau
Ah, maldita sea. Y ni siquiera está archivado en la máquina WayBack .
Jörg W Mittag
¿Cómo se supone que la persona que llama debe cumplir su parte del contrato? Parece que no hay forma de comunicar (en código) cuáles deberían ser los parámetros. Cada función tiene la forma función fname (objParam, objParam, ...). ¿Significa esto que lenguajes como javascript dependen totalmente de la documentación externa para comunicar el uso?
Legión
@Legion: documentación, buen nombre, sentido común, pruebas como especificaciones de comportamiento, lectura del código fuente, lo que sea. Tenga en cuenta que esto en realidad no es muy diferente de los sistemas de tipos más débiles, como C # o Java: por ejemplo, el significado del valor devuelto IComparer<T>.Compare(T, T)solo está claro en la documentación, no en el tipo. Y en java.util.Collections.binarySearch(java.util.List<T>)qué tipo de dice que el ...
Jörg W Mittag
1

Sé que no podría verificarlo, pero rastrear los errores de tiempo de ejecución de JavaScript puede ser una pesadilla, ya que no siempre ocurren donde el error realmente ocurre en el código.

De hecho, la práctica típica es no verificar. Y sí, esto significa que obtendrá errores de JavaScript que se informan en otras partes del problema real. Pero en la práctica, no creo que esto sea un gran problema.

Cuando trabajo en JavaScript, estoy constantemente probando lo que estoy escribiendo. En la mayoría de los códigos, tengo pruebas unitarias que se ejecutan automáticamente cada vez que guardo mi editor. Cuando algo inesperadamente sale mal, lo sé casi de inmediato. Tengo un área de código muy pequeña en la que podría haber cometido el error, ya que casi siempre es lo último que toco que tiene el error.

Cuando recibo un error de tiempo de ejecución, al menos tengo el seguimiento de la pila, y en el caso de un error en el navegador, tengo la capacidad de ir a cualquier nivel del seguimiento de la pila e inspeccionar las variables. Por lo general, es fácil rastrear el origen del valor incorrecto y, por lo tanto, rastrearlo hasta el problema original.

Si eres como yo cuando escribí principalmente en lenguajes estáticamente escritos, escribí bloques de código más grandes antes de probar y no tenía práctica en rastrear un valor de donde vino. La programación en un lenguaje como javascript es diferente, tienes que usar diferentes habilidades. Sospecho que programar así parece mucho más difícil, porque esas no son las habilidades que has desarrollado trabajando en otros lenguajes como C #.

Dicho esto, creo que hay mucho que decir sobre los tipos explícitos. Son excelentes para la documentación y la detección temprana de errores. Creo que en el futuro veremos una mayor adopción de cosas como Flow y Typecript que agregan la comprobación de tipo estático al javascript.

Winston Ewert
fuente
0

Creo que estás haciendo lo correcto, solo necesitas encontrar el estilo que sea más agradable a la vista. Aquí hay algunas ideas:

  • En lugar de que if(myObj.hasSomeProperty())puedas usar if( myobj.prop !== undefined ). Esto, por cierto, funcionará solo en modo no estricto, en modo estricto que tendría que usar if( typeof myobj.prop !== 'undefined' ).

  • Puede descargar parte de la verificación de tipos para validar por separado. Esto tiene la ventaja de poder omitir la validación una vez que las interfaces están maduras, por ejemplo if( is_valid( myobject )), dónde is_validcomienza if( !DEBUG ) return true;.

  • A veces tiene sentido clonar la entrada en una forma canónica, en cuyo caso podría recopilar los diversos objetivos de validación en la función / objeto de clonación. Por ejemplo, en my_data = Data( myobj, otherstuff )el Dataconstructor podría ejecutar convenientemente todas las diversas validaciones en un lugar central.

  • Podría usar alguna biblioteca que (con un costo de rendimiento) simplifique su validación de tipo en algo más elegante. Incluso si no va a tomar esta ruta a largo plazo, puede que le resulte cómodo llevarlo a su propio estilo sin problemas. Algunos ejemplos incluyen xtype.js , type-check , validator.js , etc.

avnr
fuente