Tipi di dato Testuali in Javascript
- In JavaScript, il tipo di dato testuale principale è la stringa, che rappresenta una sequenza di caratteri.
- Le stringhe possono essere create utilizzando virgolette singole, doppie o backtick (template literals).
- Le stringhe sono immutabili, il che significa che non possono essere modificate una volta create.
- Le stringhe hanno una lunghezza e possono essere indicizzate per accedere ai singoli caratteri.
- Le espressioni regolari sono oggetti che rappresentano modelli di ricerca nel testo e possono essere utilizzate per operazioni avanzate come la ricerca e la sostituzione.
- Le espressioni regolari possono essere create utilizzando la sintassi
/pattern/
o con il costruttoreRegExp
. - JavaScript fornisce metodi per manipolare le stringhe e lavorare con le espressioni regolari, come
match()
,replace()
,test()
, e altri.
Rappresentazione del Testo in JavaScript
Il tipo JavaScript per rappresentare il testo è la stringa. Una stringa è una sequenza ordinata immutabile di valori a 16 bit, ognuno dei quali rappresenta tipicamente un carattere Unicode. La lunghezza di una stringa è il numero di valori a 16 bit che contiene. Le stringhe di JavaScript (e i suoi array) utilizzano l'indicizzazione basata su zero: il primo valore a 16 bit è nella posizione 0, il secondo nella posizione 1, e così via. La stringa vuota è la stringa di lunghezza 0. JavaScript non ha un tipo speciale che rappresenta un singolo elemento di una stringa. Per rappresentare un singolo valore a 16 bit, si usa semplicemente una stringa che ha una lunghezza di 1.
Caratteri, Codepoint e Stringhe JavaScript
JavaScript utilizza la codifica UTF-16 del set di caratteri Unicode, e le stringhe JavaScript sono sequenze di valori a 16 bit senza segno. I caratteri Unicode più comunemente utilizzati (quelli del "piano multilingue di base") hanno codepoint che si adattano in 16 bit e possono essere rappresentati da un elemento di una stringa. I caratteri Unicode i cui codepoint non si adattano in 16 bit sono codificati utilizzando le regole di UTF-16 come una sequenza (conosciuta come "coppia surrogata") di due valori a 16 bit. Questo significa che una stringa JavaScript di lunghezza 2 (due valori a 16 bit) potrebbe rappresentare solo un singolo carattere Unicode:
let euro = "€";
let amore = "❤";
euro.length // => 1: questo carattere ha un elemento a 16 bit
amore.length // => 2: la codifica UTF-16 di ❤ è "\ud83d\udc99"
La maggior parte dei metodi di manipolazione delle stringhe definiti da JavaScript operano su valori a 16 bit, non sui caratteri. Non trattano le coppie surrogate in modo speciale, non eseguono alcuna normalizzazione della stringa, e non assicurano nemmeno che una stringa sia UTF-16 ben formata.
In ES6, tuttavia, le stringhe sono iterabili, e se si utilizza il ciclo for/of
o l'operatore ...
con una stringa, si itererà sui caratteri effettivi della stringa, non sui valori a 16 bit. Si ritornerà su questo punto più avanti.
Letterali di Stringa
Per includere una stringa in un programma JavaScript, è sufficiente racchiudere i caratteri della stringa all'interno di una coppia corrispondente di virgolette singole o doppie o backtick ('
o "
o `
). I caratteri delle virgolette doppie e i backtick possono essere contenuti all'interno di stringhe delimitate da caratteri di virgolette singole, e similmente per le stringhe delimitate da virgolette doppie e backtick. Ecco esempi di letterali di stringa:
"" // La stringa vuota: ha zero caratteri
'testing'
"3.14"
'nome="mioform"'
"Ciao, come stai?"
"π è il rapporto della circonferenza di un cerchio al suo raggio"
`"Ciao", disse lui.`
Le stringhe delimitate con backtick sono una caratteristica di ES6, e permettono alle espressioni JavaScript di essere incorporate all'interno (o interpolate in) del letterale di stringa.
Le versioni originali di JavaScript richiedevano che i letterali di stringa fossero scritti su una singola riga, ed è comune vedere codice JavaScript che crea stringhe lunghe concatenando stringhe su singola riga con l'operatore +
. A partire da ES5, tuttavia, è possibile spezzare un letterale di stringa su più righe terminando ogni riga tranne l'ultima con una barra rovesciata (\
). Né la barra rovesciata né il terminatore di riga che la seguono fanno parte del letterale di stringa. Se si ha bisogno di includere un carattere di nuova riga in un letterale di stringa con virgolette singole o doppie, si utilizza la sequenza di caratteri \n
(documentata nella sezione successiva). La sintassi con backtick di ES6 permette alle stringhe di essere spezzate su più righe, e in questo caso, i terminatori di riga fanno parte del letterale di stringa:
// Una stringa che rappresenta 2 righe scritta su una riga:
'due\nrighe'
// Una stringa su una riga scritta su 3 righe:
"una\
lunga\
riga"
// Una stringa su due righe scritta su due righe:
`il carattere di nuova riga alla fine di questa riga
è incluso letteralmente in questa stringa`
Nella programmazione JavaScript lato client, il codice JavaScript può contenere stringhe di codice HTML, e il codice HTML può contenere stringhe di codice JavaScript. Come JavaScript, HTML utilizza virgolette singole o doppie per delimitare le sue stringhe. Quindi, quando si combinano JavaScript e HTML, è una buona idea utilizzare uno stile di virgolette per JavaScript e l'altro stile per HTML. Nel seguente esempio, la stringa "Grazie" è con virgolette singole all'interno di un'espressione JavaScript, che è poi con virgolette doppie all'interno di un attributo event-handler HTML:
<button onclick="alert('Grazie')">Cliccami</button>
Sequenze di Escape nei Letterali Stringa
Il carattere backslash (\
) ha uno scopo speciale nelle stringhe JavaScript. Combinato con il carattere che lo segue, rappresenta un carattere che altrimenti non sarebbe rappresentabile all'interno della stringa. Ad esempio, \n
è una sequenza di escape che rappresenta un carattere di nuova riga.
Un altro esempio, menzionato in precedenza, è l'escape \'
, che rappresenta il carattere virgoletta singola (o apostrofo). Questa sequenza di escape è utile quando si ha bisogno di includere un apostrofo in un letterale stringa che è contenuto all'interno di virgolette singole. Si può vedere perché queste sono chiamate sequenze di escape: il backslash permette di scappare dall'interpretazione usuale del carattere virgoletta singola. Invece di usarlo per segnare la fine della stringa, lo si usa come apostrofo:
'Hai ragione, non può essere una virgoletta'
La Tabella seguente elenca le sequenze di escape JavaScript e i caratteri che rappresentano. Tre sequenze di escape sono generiche e possono essere usate per rappresentare qualsiasi carattere specificando il suo codice carattere Unicode come numero esadecimale. Ad esempio, la sequenza \xA9
rappresenta il simbolo del copyright, che ha la codifica Unicode data dal numero esadecimale A9. Allo stesso modo, l'escape \u
rappresenta un carattere Unicode arbitrario specificato da quattro cifre esadecimali o da una a cinque cifre quando le cifre sono racchiuse in parentesi graffe: \u03c0
rappresenta il carattere π, ad esempio, e \u{1f600}
rappresenta l'emoji "faccina sorridente".
Sequenza | Carattere rappresentato |
---|---|
\0 |
Il carattere NUL (\u0000 ) |
\b |
Backspace (\u0008 ) |
\t |
Tab orizzontale (\u0009 ) |
\n |
Nuova riga (\u000A ) |
\v |
Tab verticale (\u000B ) |
\f |
Avanzamento modulo (\u000C ) |
\r |
Ritorno carrello (\u000D ) |
\" |
Virgoletta doppia (\u0022 ) |
\' |
Apostrofo o virgoletta singola (\u0027 ) |
\\ |
Backslash (\u005C ) |
\x nn |
Il carattere Unicode specificato dalle due cifre esadecimali nn |
\u nnnn |
Il carattere Unicode specificato dalle quattro cifre esadecimali nnnn |
\u{ n} |
Il carattere Unicode specificato dal codepoint n, dove n è da una a sei cifre esadecimali tra 0 e 10FFFF (ES6) |
Se il carattere \
precede qualsiasi carattere diverso da quelli mostrati nella tabella precedente, il backslash viene semplicemente ignorato (anche se le versioni future del linguaggio potrebbero, naturalmente, definire nuove sequenze di escape). Ad esempio, \#
è lo stesso di #
. Infine, come notato in precedenza, ES5 permette un backslash prima di un'interruzione di riga per spezzare un letterale stringa su più righe.
Lavorare con le Stringhe
Una delle funzionalità integrate di JavaScript è la capacità di concatenare stringhe. Se si usa l'operatore +
con i numeri, li somma. Ma se si usa questo operatore sulle stringhe, le unisce aggiungendo la seconda alla prima. Per esempio:
let msg = "Ciao, " + "mondo"; // Produce la stringa "Ciao, mondo"
let saluto = "Benvenuto nel mio sito," + " " + nome;
Le stringhe possono essere confrontate con gli operatori standard di uguaglianza ===
e disuguaglianza !==
: due stringhe sono uguali se e solo se consistono esattamente della stessa sequenza di valori a 16-bit. Le stringhe possono anche essere confrontate con gli operatori <
, <=
, >
, e >=
. Il confronto delle stringhe viene fatto semplicemente confrontando i valori a 16-bit.
Per determinare la lunghezza di una stringa, ossia il numero di valori a 16-bit che contiene, si usa la proprietà length
della stringa:
s.length
Oltre a questa proprietà length
, JavaScript fornisce un ricco set di API per lavorare con le stringhe:
let s = "Ciao, mondo"; // Iniziamo con del testo.
// Ottenere porzioni di una stringa
s.substring(1,4) // => "iao": il 2°, 3°, e 4° carattere.
s.slice(1,4) // => "iao": stessa cosa
s.slice(-3) // => "ndo": ultimi 3 caratteri
s.split(", ") // => ["Ciao", "mondo"]: dividi alla stringa delimitatore
// Ricercare una stringa
s.indexOf("l") // => -1: posizione della prima lettera l
s.indexOf("o", 3) // => 10: posizione della prima "o" a o dopo 3
s.indexOf("zz") // => -1: s non include la sottostringa "zz"
s.lastIndexOf("o") // => 10: posizione dell'ultima lettera o
// Funzioni di ricerca booleane in ES6 e successivi
s.startsWith("Cia") // => true: la stringa inizia con questi
s.endsWith("!") // => false: s non finisce con quello
s.includes("ao") // => true: s include la sottostringa "ao"
// Creare versioni modificate di una stringa
s.replace("iao", "ello") // => "Chello, mondo"
s.toLowerCase() // => "ciao, mondo"
s.toUpperCase() // => "CIAO, MONDO"
s.normalize() // Normalizzazione Unicode NFC: ES6
s.normalize("NFD") // Normalizzazione NFD. Anche "NFKC", "NFKD"
// Ispezionare caratteri individuali (16-bit) di una stringa
s.charAt(0) // => "C": il primo carattere
s.charAt(s.length-1) // => "o": l'ultimo carattere
s.charCodeAt(0) // => 67: numero a 16-bit alla posizione specificata
s.codePointAt(0) // => 67: ES6, funziona per codepoint > 16 bit
// Funzioni di padding delle stringhe in ES2017
"x".padStart(3) // => " x": aggiungi spazi a sinistra fino a lunghezza 3
"x".padEnd(3) // => "x ": aggiungi spazi a destra fino a lunghezza 3
"x".padStart(3, "*") // => "**x": aggiungi stelle a sinistra fino a lunghezza 3
"x".padEnd(3, "-") // => "x--": aggiungi trattini a destra fino a lunghezza 3
// Funzioni di rimozione spazi. trim() è ES5; altri ES2019
" test ".trim() // => "test": rimuovi spazi all'inizio e alla fine
" test ".trimStart() // => "test ": rimuovi spazi a sinistra. Anche trimLeft
" test ".trimEnd() // => " test": rimuovi spazi a destra. Anche trimRight
// Metodi vari per stringhe
s.concat("!") // => "Ciao, mondo!": usa semplicemente l'operatore + invece
"<>".repeat(5) // => "<><><><><>": concatena n copie. ES6
Si ricorda che le stringhe sono immutabili in JavaScript. Metodi come replace()
e toUpperCase()
restituiscono nuove stringhe: non modificano la stringa su cui vengono invocati.
Le stringhe possono anche essere trattate come array di sola lettura, e si può accedere ai caratteri individuali (valori a 16-bit) da una stringa usando le parentesi quadre invece del metodo charAt()
:
let s = "ciao, mondo";
s[0] // => "c"
s[s.length-1] // => "o"
Interpolazione delle Stringhe
In ES6 e versioni successive, i letterali stringa possono essere delimitati con backtick:
let s = `ciao mondo`;
Questa è più di una semplice sintassi alternativa per i letterali stringa, tuttavia, perché adoperando i cosiddetti template literals si possono includere espressioni JavaScript arbitrarie. Il valore finale di un letterale stringa tra backtick viene calcolato (o interpolato) valutando qualsiasi espressione inclusa, convertendo i valori di quelle espressioni in stringhe e combinando quelle stringhe calcolate con i caratteri letterali all'interno dei backtick:
let nome = "Marco";
let saluto = `Ciao ${ nome }.`; // saluto == "Ciao Marco."
Tutto ciò che si trova tra ${
e la }
corrispondente viene interpretato come un'espressione JavaScript. Tutto ciò che si trova al di fuori delle parentesi graffe è normale testo letterale stringa. L'espressione all'interno delle parentesi graffe viene valutata e poi convertita in una stringa e inserita nel template, sostituendo il simbolo del dollaro, le parentesi graffe e tutto ciò che si trova tra di esse.
Un template literal può includere qualsiasi numero di espressioni. Può utilizzare qualsiasi carattere di escape che le stringhe normali possono utilizzare, e può estendersi su qualsiasi numero di righe, senza necessità di escape speciali. Il seguente template literal include quattro espressioni JavaScript, una sequenza di escape Unicode e almeno quattro newline (i valori delle espressioni potrebbero includere anche newline):
let messaggioErrore = `\
\u2718 Fallimento test in ${nomeFile}:${numeroRiga}:
${eccezione.message}
Stack trace:
${eccezione.stack}
`;
Il backslash alla fine della prima riga qui effettua l'escape del newline iniziale in modo che la stringa risultante inizi con il carattere Unicode ✘ (\u2718
) piuttosto che con un newline.
Template literal taggati
Una funzionalità potente ma meno comunemente utilizzata dei template literal è che, se un nome di funzione (o "tag") viene posizionato proprio prima del backtick di apertura, allora il testo e i valori delle espressioni all'interno del template literal vengono passati alla funzione. Il valore di questo "template literal taggato" è il valore di ritorno della funzione. Questo potrebbe essere utilizzato, ad esempio, per applicare l'escape HTML o SQL ai valori prima di sostituirli nel testo.
ES6 ha una funzione tag integrata: String.raw()
. Restituisce il testo all'interno dei backtick
senza alcuna elaborazione degli escape con backslash:
`\n`.length // => 1: la stringa ha un singolo carattere di nuova riga
String.raw`\n`.length // => 2: un carattere backslash e la lettera n
Si noti che anche se la parte tag di un template literal taggato è una funzione, non vengono utilizzate parentesi nella sua invocazione. In questo caso molto specifico, i caratteri backtick sostituiscono le parentesi aperte e chiuse.
La capacità di definire le proprie funzioni tag template è una funzionalità potente di JavaScript. Queste funzioni non devono necessariamente restituire stringhe, e possono essere utilizzate come costruttori, come se definissero una nuova sintassi letterale per il linguaggio. Si studierà questa funzionalità in dettaglio nelle lezioni successive.
Pattern Matching
JavaScript definisce un tipo di dato noto come espressione regolare (o RegExp
) per descrivere e abbinare pattern nelle stringhe di testo. Le RegExp
non sono uno dei tipi di dato fondamentali in JavaScript, ma hanno una sintassi letterale come i numeri e le stringhe, quindi a volte sembrano essere fondamentali. La grammatica dei letterali delle espressioni regolari è complessa e l'API che definiscono è non banale. Si studieranno più avanti.
Poiché le RegExp
sono potenti e comunemente utilizzate per l'elaborazione del testo, tuttavia, questa sezione fornisce una breve panoramica.
Il testo tra una coppia di barre costituisce un letterale di espressione regolare. La seconda barra nella coppia può anche essere seguita da una o più lettere, che modificano il significato del pattern. Per esempio:
// Abbina le lettere H T M L all'inizio di una stringa
/^HTML/;
// Abbina una cifra diversa da zero,
// seguita da qualsiasi # di cifre
/[1-9][0-9]*/;
// Abbina "javascript" come parola,
// senza distinzione tra maiuscole e minuscole
/\bjavascript\b/i;
Gli oggetti RegExp
definiscono diversi metodi utili, e anche le stringhe hanno metodi che accettano argomenti RegExp
. Per esempio:
// Testo di esempio
let testo = "testing: 1, 2, 3";
// Abbina tutte le istanze di una o più cifre
let pattern = /\d+/g;
// Eseguiamo varie operazioni con l'espressione regolare
pattern.test(testo) // => true: esiste una corrispondenza
testo.search(pattern) // => 9: posizione della prima corrispondenza
testo.match(pattern) // => ["1", "2", "3"]: array di tutte le corrispondenze
testo.replace(pattern, "#") // => "testing: #, #, #"
testo.split(/\D+/) // => ["","1","2","3"]: dividi su non-cifre