In questa serie di articoli, abbiamo parlato della coercizione di tipo, di come si differenzia dalla conversione di tipi e di come viene eseguita nelle lingue digitate dinamicamente.
Se ti stai appena iscrivendo alla serie, dai un'occhiata agli articoli precedenti per assicurarti di essere al passo con la situazione attuale:
Mentre l'altro articolo si concentra su lingue e tipi di dati debolmente tipizzati ad alto livello, esamineremo alcuni esempi specifici di coercizione di tipo in un linguaggio tipizzato debolmente e le insidie che potremmo sperimentare senza sapere come funziona la coercizione di tipo e come può ritorcersi contro.
In particolare, esamineremo diversi esempi utilizzando JavaScript e, sebbene i risultati che potresti vedere attraverso l'uso degli esempi non tradurranno necessariamente 1: 1 in altre lingue, forniranno comunque una serie di test che puoi eseguire qualsiasi cosa tu stia utilizzando nei tuoi progetti quotidiani o nei tuoi progetti e valutare i risultati che stai vedendo.
Probabilmente, uno dei problemi più comuni che si verificano nelle lingue debolmente tipizzate arriva quando facciamo confronti. Certo, ci sono altre volte che aspettarsi che una variabile sia di un tipo quando è davvero un'altra può influire negativamente su di noi, ma i problemi più comuni si verificano ogni volta che eseguiamo qualche tipo di confronto.
Questi confronti possono presentarsi sotto forma di operazioni di uguaglianza, operazioni condizionali, operazioni bit a bit o durante scatola dell'interruttore
operazioni.
Come menzionato nei precedenti articoli di questa serie, lingue diverse hanno diversi modi in cui vanno a forzare i tipi di dati in modo che gli esempi che stiamo guardando in questo articolo possano differire un po 'nel lavoro che fai.
Cioè, guarderemo questi esempi usando JavaScript poiché è una lingua così diffusa, ma le regole sono ancora applicabili in altre lingue - è solo che altre lingue potrebbero mettere una priorità diversa su un tipo di dati piuttosto che un'altra, quindi i risultati della coercizione potrebbero essere leggermente diversi.
Detto questo, iniziamo a esaminare come JavaScript gestisce i confronti tra tipi di dati usando l'operatore di uguaglianza (==
), l'operatore rigoroso di uguaglianza (===
) e quando si usano valori come non definito
e nullo
.
Prima di esaminare i confronti tra diversi tipi di dati, prendiamo un momento per notare che, in JavaScript, non definito
e nullo
sono due diversi tipi di valori. Come se non bastasse, può diventare ancora più confuso quando si confrontano i due valori.
Innanzitutto, nota quanto segue:
typeof (indefinito)
nella console, quindi il risultato sarebbe non definito
.typeof (null)
nella console, quindi il risultato sarebbe oggetto
.Quindi, se dovessi dichiarare una variabile senza effettivamente assegnargli un valore, e dovresti valutare il suo tipo, allora vedresti non definito
.
/ ** * Dichiara una variabile 'nome' ma non assegnargli un valore. * Esegui il risultato di 'typeof' su una console e ti verrà * dato 'indefinito'. * / var name; typeof (nome);
Quindi, diciamo che optiamo per inizializzare la variabile con un nullo
valore. Se dovessi valutare la variabile usando tipo di
ti verrebbe dato un oggetto
risultato.
// Innanzitutto, dichiareremo il numero variabile var; / ** * A questo punto, se dovessimo valutare la variabile * usando typeof, allora ci sarebbe dato 'indefinito'. * / // Assegna ora alla variabile un valore di 'null' numero = null; / ** * E valutare la variabile. A questo punto, * verrà restituito il valore di "oggetto". * / typeof (numero);
Confondere? Ricordiamo da prima che in JavaScript, nullo
è un oggetto
dove undefined è un suo tipo - non definito
.
Detto questo, ora possiamo effettivamente vedere perché i confronti possono diventare difficili ogni volta che eseguiamo confronti su valori senza conoscere esplicitamente il loro tipo.
In questa sezione, daremo un'occhiata ai risultati del confronto di valori di tipi diversi, ma vediamo come vengono valutati gli uni contro gli altri utilizzando sia confronti di uguaglianza che confronti rigorosi di uguaglianza.
Nota che tutti gli esempi che stiamo elencando di seguito dovrebbero essere in grado di essere eseguiti all'interno di una console del browser come Firebug o gli Strumenti per sviluppatori di Chrome.
Per cominciare, inizieremo con non definito
e nullo
.
// Restituisce vero undefined == null; null == undefined; // Restituisce false undefined === null; null === non definito;
Si noti che nel primo caso, l'operatore di uguaglianza restituisce un valore del confronto dopo eseguendo la coercizione di tipo. Cioè, l'interprete sta facendo la sua migliore ipotesi su cosa intendiamo quando eseguiamo questo confronto.
Nel secondo caso, stiamo usando l'operatore di uguaglianza rigorosa. In questo caso, non si verifica alcuna coercizione di tipo. Invece, sta prendendo i valori esattamente come sono e confrontandoli.
Quindi, diamo un'occhiata alla dichiarazione di una variabile, non assegnandole un valore e quindi eseguendo un confronto.
// Dichiara la variabile, non assegnargli un valore var esempio; // Se confrontato con undefined, restituisce true in entrambi i casi example == undefined; example === undefined // Se confrontato con null, restituisce true o false example == null; // true example === null; // false // Assegna un valore alla variabile example = null; // Ora fai un esempio di confronto rigoroso === null; // Restituisce vero
Come puoi vedere, le cose cominciano a diventare un po 'più complicate quando iniziamo a dichiarare e confrontare le variabili con o senza valori.
Una volta che iniziamo a introdurre stringhe, numeri e valori booleani, può diventare ancora più complicato. Innanzitutto, iniziamo con stringhe e numeri. Inizieremo dichiarando una variabile con un valore stringa di 42
e un numero con il 42
e poi faremo i nostri confronti.
var sNumero, iNumero; sNumber = '42'; iNumero = 42; // I confronti di uguaglianza producono il vero sNumber == iNumero; // Il confronto rigoroso produce false sNumber === iNumero;
Ancora una volta, nota nel primo caso, l'interprete tenta di forzare i valori delle variabili e quindi confrontarle. Nel primo caso, funziona - stiamo confrontando un valore di stringa di 42
a un valore numerico di 42
, ma quando usiamo il paragone della parità e otteniamo falso
.
Il secondo caso è tecnicamente più accurato perché il primo valore è una stringa e il secondo è un numero. Confrontare due valori di tipi diversi dovrebbe sempre dare false.
Anche se abbiamo dato un'occhiata a questo in un precedente articolo, per quanto riguarda il caso di numeri e booleani?
var iNumber, bBoolean; iNumber = 0; bBooleano = falso; // Restituisce true iNumber == bBoolean; // Restituisce false iNumber === bBoolean; // Restituisce true iNumber = 1; bBoolean = true; iNumber == bBoolean; // Restituisce false iNumber === bBoolean;
A questo punto, dovresti iniziare a notare un modello: ogni volta che confronti valori di tipi diversi, JavaScript può forzare correttamente i valori, ma produce il risultato più accurato quando usi l'operatore di uguaglianza rigorosa.
Infine, diamo un'occhiata a un esempio che combina stringhe, numeri e booleani.
var sEsempio, iEsempio, bEsempio; sEsempio = '1'; i Esempio = 1; bEsempio = true; // Restituisce true sExample == iEsempio; // Restituisce false sEsempio === iEsempio; // Restituisce true iExample == bExample; // Restituisce false iExample === bEsempio; // Restituisce true sExample == bExample; // Restituisce false sEsempio === bEsempio;
Si noti che questi sono confronti di base; tuttavia, quando fatto nel contesto di un se altro
o se / else se / else
vedi come può interrompere il flusso del controllo attraverso il condizionale.
Si noti che quando si eseguono operazioni logiche come &&
e ||
così come operatori bit a bit come &
e |
che le regole di coercizione si applicano ancora. A tal fine, assicurati che quando esegui tali operazioni, utilizzi valori dello stesso tipo per ottenere i risultati più precisi.
Altrimenti, la coercizione può risultare in un falso positivo o in un falso negativo.
Questo completa la nostra rapida analisi del principiante sui tipi di dati e sulla coercizione di tipo in un linguaggio tipizzato dinamicamente. In definitiva, le regole pratiche consistono nell'utilizzare sempre operatori rigorosi di uguaglianza e assicurarsi che le variabili con le quali si sta lavorando siano dello stesso tipo. Se non sei sicuro, puoi sempre convertirli esplicitamente usando le strategie che abbiamo delineato in precedenza nella serie.
Nel corso di questa serie, abbiamo esaminato il modo in cui i tipi variano e si comportano da linguaggi fortemente tipizzati a lingue debolmente tipizzate. Abbiamo esaminato il modo in cui casting e coercizione differiscono, e abbiamo osservato alcune delle potenziali insidie che questo potrebbe comportare affidandosi troppo all'interprete o al compilatore quando si effettuano i confronti.
Infine, abbiamo dato un'occhiata ad alcune strategie su come scrivere un codice più difensivo assicurandoci di avere il tipo di dati di cui abbiamo bisogno e su come usare rigorosi operatori di confronto per assicurarci di ottenere i risultati di cui abbiamo bisogno.
Come accennato in precedenza, abbiamo utilizzato JavaScript ai fini dei nostri esempi in questo serio articolo, ma altre lingue debolmente tipizzate sono soggette alle stesse insidie. Il modo in cui danno priorità alle loro coercizioni variano, ma le strategie delineate in questa serie dovrebbero aiutare a scrivere codice più resiliente quando si lavora in lingue debolmente tipizzate.