Come si utilizza la digitazione anatra in javascript senza sempre verificare proprietà e metodi?


11

So che JavaScript utilizza la tipizzazione duck e all'inizio ho pensato che questo avrebbe reso il polimorfismo facile rispetto a linguaggi fortemente tipizzati come C #. Ma ora le mie funzioni che accettano argomenti sono disseminate di cose come:

if(myObj.hasSomeProperty())

o

if(myObj.hasSomeMethod())

o

if(isNumber(myParam))

eccetera.

Questo è davvero brutto per me. Vengo da uno sfondo C # e trovo che le interfacce definite siano molto migliori.

Mi chiedo se sto tentando erroneamente di applicare strategie efficaci in linguaggi tipicamente statici e c'è un modo migliore per farlo in javascript?

So che non potrei semplicemente controllare, ma rintracciare gli errori di runtime javascript può essere un incubo poiché non sempre si verificano dove l'errore si sta effettivamente verificando nel codice.


2
Penso che potresti semplicemente armeggiare sulla natura di un linguaggio tipizzato in modo dinamico. È necessario abituarsi alla mentalità che si verificheranno molti errori in fase di esecuzione anziché in fase di compilazione. Se senti la necessità di verificare se ogni argomento è un numero in ogni funzione che immette numeri, può diventare piuttosto un peso (anche se forse vale la pena se stai spedendo una lib con la sicurezza come obiettivo principale). Per qualsiasi cosa di scala, trovo che sia essenziale consentire solo che funzioni falliscano se vengono passati tipi sbagliati. Invece, un focus più produttivo potrebbe essere sulla costruzione di test.

Laddove potrebbe essere utile eseguire questi controlli per assicurarsi che i tipi siano conformi ai requisiti di interfaccia necessari (verificare se dispongono dei metodi richiesti, ad esempio) è nelle funzioni più centrali e ampiamente utilizzate (quelle con instabilità = 0 con il metrica di accoppiamento efferente / afferente fornita da Martin). Questo dovrebbe essere un obiettivo piuttosto piccolo. Di solito ci sono molte funzioni locali una tantum che sono isolate nell'ambito - quelle probabilmente non hanno bisogno di un insieme così completo di controlli di runtime. Non accumulano molta complessità.

Passa a Tipo script. È ancora dattiloscritto, ma supporta la tipizzazione statica per rilevare molti errori in fase di compilazione.
CodesInChaos,

2
Hai colpito il singolo più grande problema della battitura di anatra: il suo potere deriva dalla sua debolezza. Se vuoi fare JavaScript orientato agli oggetti, devi solo convivere con gli errori di runtime e sperare che i test unitari li trovino subito dopo averli creati :-(
Ross Patterson,

@RossPatterson Il problema dei PO è con la digitazione dinamica, non con la digitazione anatra. TypeScript e Go sono entrambi duck, ma evitano il problema dell'OP. Il problema con la tipizzazione delle anatre è diverso, vale a dire che puoi avere membri che superano il test delle anatre ma non soddisfano il contratto che ti aspetti.
Codici InCos

Risposte:


11

Come si utilizza la digitazione anatra in javascript senza sempre verificare proprietà e metodi?

Semplice: non sempre verificare proprietà e metodi.

In Ruby, quello che stai chiamando si chiama "tipizzazione di pollo". In un linguaggio digitato dinamicamente da papera, ti fidi semplicemente che il chiamante ti passa un oggetto adatto. È compito del chiamante onorare la sua parte del contratto.

So che JavaScript utilizza la tipizzazione duck e all'inizio ho pensato che questo avrebbe reso il polimorfismo facile rispetto a linguaggi fortemente tipizzati come C #.

Qui stai confondendo più assi ortogonali di battitura. Esistono quattro assi ortogonali di battitura:

  • Quando : digitazione dinamica (i tipi non sono noti e controllati fino al runtime) rispetto alla digitazione statica (i tipi sono noti e controllati prima del runtime)
  • Cosa : digitazione anatra (i tipi si basano sul comportamento ), tipizzazione strutturale (i tipi si basano sulla struttura ) e tipizzazione nominale (i tipi si basano sul nome )
  • Li vedi? digitazione esplicita (i tipi devono essere esplicitamente annotati) vs. digitazione implicita (i tipi sono dedotti)
  • digitazione forte vs. digitazione debole - potresti aver notato che non ho dato a questo un titolo accattivante né una spiegazione tra parentesi, perché a differenza dei sette termini sopra, che hanno ciascuno una definizione precisa accettata universalmente, questi due termini hanno circa una dozzina di definizioni vaghe semi-ampiamente usate che si contraddicono a vicenda; idealmente dovresti evitare del tutto questi termini e, se devi usarli, definiscili prima

Dal momento che hai citato C #: è tipicamente tipicamente statico, ma supporta la tipizzazione dinamica attraverso il tipo dynamic, è principalmente tipicamente nominale, ma i tipi anonimi usano la digitazione strutturale e i modelli sintattici (come la sintassi di comprensione delle query LINQ) possono essere argomentati come anatra di tipo o tipicamente strutturato, è per lo più tipizzato in modo esplicito ma supporta la tipizzazione implicita per argomenti di tipo generico e variabili locali (anche se il caso della variabile locale è piuttosto strano rispetto alla maggior parte delle altre lingue, perché non puoi semplicemente lasciare il tipo fuori, invece devi dagli uno pseudo-tipo esplicitovar, in altre parole, se si desidera un tipo implicito, è necessario dirlo esplicitamente). Il fatto che C # sia tipizzato in modo forte o debole è una questione di quale definizione dei due termini utilizzati, tuttavia, si noti che ci possono essere molti errori di runtime in C #, soprattutto a causa della covarianza di array non sicura.

So che non potrei semplicemente controllare, ma rintracciare gli errori di runtime javascript può essere un incubo poiché non sempre si verificano dove l'errore si sta effettivamente verificando nel codice.

Il debug non è un'abilità facile da imparare. Esistono, tuttavia, tecniche per facilitare il debug, ad esempio Saff Squeeze è una tecnica descritta da Kent Beck che utilizza test e refactoring per il debug:

Hit 'em High, Hit' em Low :

Test di regressione e Saff Squeeze

Kent Beck, Three Rivers Institute

Riassunto: per isolare efficacemente un difetto, iniziare con un test a livello di sistema e progressivamente in linea e potare fino ad avere il test più piccolo possibile che dimostri il difetto.


Quel link hit hit hit low ottiene un http 500 per me, con "Pagina non più disponibile" come messaggio orientato all'uomo.
joshp,

Il dominio threeriversinstitute.org sembra essere stato abbandonato.
Bart van Ingen Schenau,

Ah, dannazione. E non è nemmeno archiviato sulla WayBack Machine .
Jörg W Mittag,

In che modo il chiamante dovrebbe onorare la propria parte del contratto? Sembra che non ci sia modo di comunicare (nel codice) quali dovrebbero essere i parametri. Ogni funzione ha il nome funzione fname (objParam, objParam, ...). Questo significa che lingue come javascript dipendono totalmente dalla documentazione esterna per comunicare l'utilizzo?
Legione,

@Legion: documentazione, buona denominazione, buon senso, test come specifiche comportamentali, lettura del codice sorgente, nome. Si noti che questo in realtà non è molto diverso dai sistemi di tipo più debole come quelli di C # o Java: ad esempio, il significato del valore di ritorno di IComparer<T>.Compare(T, T)è chiaro solo dalla documentazione, non dal tipo. E dove nel tipo di java.util.Collections.binarySearch(java.util.List<T>)dice che ...
Jörg W Mittag il

1

So che non potrei semplicemente controllare, ma rintracciare gli errori di runtime javascript può essere un incubo poiché non sempre si verificano dove l'errore si sta effettivamente verificando nel codice.

In effetti, la pratica tipica non è quella di verificare. E, sì, questo significa che otterrai errori javascript che sono riportati altrove dal problema reale. Ma in pratica, non trovo che questo sia un grosso problema.

Quando lavoro in javascript, collaudo costantemente ciò che sto scrivendo. Nella maggior parte del codice, ho dei test unitari che vengono eseguiti automaticamente ogni volta che salvo il mio editor. Quando qualcosa inaspettatamente va storto, lo so quasi immediatamente. Ho una piccola area di codice in cui avrei potuto commettere l'errore, dato che è quasi sempre l'ultima cosa che ho toccato ad avere l'errore.

Quando ricevo un errore di runtime, ho almeno la traccia dello stack e, nel caso di un errore nel browser, ho la possibilità di passare a qualsiasi livello della traccia dello stack e ispezionare le variabili. In genere è facile risalire da dove proviene il valore errato e quindi risalire al problema originale.

Se sei come me quando ho scritto principalmente in linguaggi tipicamente statici, ho scritto blocchi di codice più grandi prima del test e non avevo pratica nel rintracciare un valore da cui proveniva. Programmare in un linguaggio come javascript è diverso, devi usare abilità diverse. Sospetto che programmare in questo modo sembri molto più difficile, perché quelle non sono le competenze che hai sviluppato lavorando in altri linguaggi come C #.

Detto questo, penso che ci sia molto da dire per tipi espliciti. Sono ottimi per la documentazione e la rilevazione tempestiva di errori. Penso che in futuro vedremo l'adozione crescente di cose come Flow e Typescript che aggiungono il controllo del tipo statico al javascript.


0

Penso che stai facendo la cosa giusta, devi solo trovare lo stile che sarà più piacevole per i tuoi occhi. Ecco alcune idee:

  • Invece di if(myObj.hasSomeProperty())te potresti usare if( myobj.prop !== undefined ). Questo, BTW funzionerà solo in modalità non rigorosa, in modalità rigorosa che dovresti usare if( typeof myobj.prop !== 'undefined' ).

  • È possibile scaricare parte del controllo del tipo su validatori separati. Ciò ha il vantaggio di essere in grado di saltare la convalida una volta che le interfacce sono mature, ad esempio if( is_valid( myobject )), da dove is_validinizia if( !DEBUG ) return true;.

  • A volte ha senso clonare l'input in una forma canonica, nel qual caso è possibile raccogliere i vari target di validazione nella funzione / oggetto di clonazione. Per exmaple, my_data = Data( myobj, otherstuff )nel Datacostruttore potrebbe convenientemente eseguire tutte le varie convalide in un posto centrale.

  • È possibile utilizzare alcune librerie che ottimizzeranno (con un pedaggio delle prestazioni) la convalida del tipo in qualcosa di più elegante. Anche se non seguirai questo percorso a lungo termine, potresti trovarti a tuo agio nel metterti agevolmente nel tuo stile. Alcuni esempi includono xtype.js , type-check , validator.js , ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.