JavaScript utilizza stringhe immutabili o mutabili? Ho bisogno di un "costruttore di stringhe"?
JavaScript utilizza stringhe immutabili o mutabili? Ho bisogno di un "costruttore di stringhe"?
Risposte:
Sono immutabili. Non puoi cambiare un carattere all'interno di una stringa con qualcosa di simile var myString = "abbdef"; myString[2] = 'c'
. I metodi di manipolazione delle stringhe come trim
, slice
restituiscono nuove stringhe.
Allo stesso modo, se si hanno due riferimenti alla stessa stringa, la modifica di una non influisce sull'altra
let a = b = "hello";
a = a + " world";
// b is not affected
Tuttavia, ho sempre sentito ciò che Ash ha menzionato nella sua risposta (che l'utilizzo di Array.join è più veloce per la concatenazione), quindi ho voluto testare i diversi metodi di concatenazione di stringhe e di astrazione del modo più veloce in StringBuilder. Ho scritto alcuni test per vedere se questo è vero (non lo è!).
Questo era quello che credevo fosse il modo più veloce, anche se continuavo a pensare che l'aggiunta di una chiamata al metodo potesse rallentare ...
function StringBuilder() {
this._array = [];
this._index = 0;
}
StringBuilder.prototype.append = function (str) {
this._array[this._index] = str;
this._index++;
}
StringBuilder.prototype.toString = function () {
return this._array.join('');
}
Ecco i test di velocità delle prestazioni. Tutti e tre creano una gigantesca stringa composta da concatenare "Hello diggity dog"
centomila volte in una stringa vuota.
Ho creato tre tipi di test
Array.push
eArray.join
Array.push
, quindi utilizzoArray.join
Quindi ho creato gli stessi tre test astrandoli in StringBuilderConcat
, StringBuilderArrayPush
e StringBuilderArrayIndex
http://jsperf.com/string-concat-without-sringbuilder/5 Per favore, vai lì ed esegui i test in modo che possiamo ottenere un bel campione. Nota che ho corretto un piccolo bug, quindi i dati per i test sono stati cancellati, aggiornerò la tabella una volta che ci saranno abbastanza dati sulle prestazioni. Vai su http://jsperf.com/string-concat-without-sringbuilder/5 per la vecchia tabella di dati.
Ecco alcuni numeri (ultimo aggiornamento di Ma5rch 2018), se non vuoi seguire il link. Il numero su ogni test è in 1000 operazioni / secondo ( più alto è meglio )
| Browser | Index | Push | Concat | SBIndex | SBPush | SBConcat |
---------------------------------------------------------------------------
| Chrome 71.0.3578 | 988 | 1006 | 2902 | 963 | 1008 | 2902 |
| Firefox 65 | 1979 | 1902 | 2197 | 1917 | 1873 | 1953 |
| Edge | 593 | 373 | 952 | 361 | 415 | 444 |
| Exploder 11 | 655 | 532 | 761 | 537 | 567 | 387 |
| Opera 58.0.3135 | 1135 | 1200 | 4357 | 1137 | 1188 | 4294 |
I risultati
Al giorno d'oggi, tutti i browser sempreverdi gestiscono bene la concatenazione di stringhe. Array.join
aiuta solo IE 11
Nel complesso, Opera è la più veloce, 4 volte più veloce di Array.join
Firefox è secondo ed Array.join
è solo leggermente più lento in FF ma considerevolmente più lento (3x) in Chrome.
Chrome è terzo, ma il concat per stringhe è 3 volte più veloce di Array.join
La creazione di StringBuilder sembra non influire troppo sulle prestazioni.
Spero che qualcun altro lo trovi utile
Caso di prova diverso
Poiché @RoyTinker pensava che il mio test fosse imperfetto, ho creato un nuovo caso che non crea una grande stringa concatenando la stessa stringa, ma utilizza un carattere diverso per ogni iterazione. La concatenazione delle stringhe sembrava ancora più veloce o altrettanto veloce. Facciamo quei test in esecuzione.
Suggerisco a tutti di continuare a pensare ad altri modi per testarlo e di sentirsi liberi di aggiungere nuovi collegamenti a diversi casi di test di seguito.
join
concatenazione di stringhe vs, quindi costruendo l'array prima del test. Non penso che sia barare se questo obiettivo è compreso (ed join
enumera l'array internamente, quindi non è barare anche per omettere un for
loop dal join
test).
Array.join
dal libro del rinoceronte :
In JavaScript, le stringhe sono oggetti immutabili, il che significa che i caratteri al loro interno non possono essere modificati e che qualsiasi operazione sulle stringhe crea effettivamente nuove stringhe. Le stringhe sono assegnate per riferimento, non per valore. In generale, quando un oggetto viene assegnato per riferimento, una modifica apportata all'oggetto tramite un riferimento sarà visibile attraverso tutti gli altri riferimenti all'oggetto. Poiché le stringhe non possono essere modificate, tuttavia, puoi avere più riferimenti a un oggetto stringa e non preoccuparti che il valore della stringa cambi senza che tu lo sappia
null
undefined
number
e boolean
. Le stringhe sono assegnate per valore e non per riferimento e vengono passate come tali. Pertanto, le stringhe non sono solo immutabili, sono un valore . Cambiare la stringa "hello"
per essere "world"
è come decidere che da ora in poi il numero 3 è il numero 4 ... non ha senso.
var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
String
oggetti creati utilizzando il costruttore di stringhe sono wrapper attorno ai valori di stringa JavaScript. Puoi accedere al valore di stringa del tipo boxed usando la .valueOf()
funzione - questo vale anche per Number
oggetti e valori numerici. È importante notare che gli String
oggetti creati usando new String
non sono stringhe effettive ma sono wrapper o caselle attorno alle stringhe. Vedi es5.github.io/#x15.5.2.1 . Informazioni su come le cose si convertono in oggetti vedi es5.github.io/#x9.9
Suggerimento sulle prestazioni:
Se è necessario concatenare stringhe di grandi dimensioni, inserire le parti di stringa in un array e utilizzare il Array.Join()
metodo per ottenere la stringa complessiva. Questo può essere molte volte più veloce per concatenare un gran numero di stringhe.
Non c'è StringBuilder
in JavaScript.
Giusto per chiarire menti semplici come la mia (da MDN ):
Immutabili sono gli oggetti il cui stato non può essere modificato una volta creato l'oggetto.
String e numeri sono immutabili.
Immutabile significa che:
È possibile fare in modo che un nome di variabile punti a un nuovo valore, ma il valore precedente rimane comunque in memoria. Da qui la necessità di raccolta dei rifiuti.
var immutableString = "Hello";
// Nel codice sopra, viene creato un nuovo oggetto con valore stringa.
immutableString = immutableString + "World";
// Ora stiamo aggiungendo "Mondo" al valore esistente.
Sembra che stiamo mutando la stringa 'immutableString', ma non lo siamo. Anziché:
Quando si aggiunge "immutableString" con un valore stringa, si verificano i seguenti eventi:
- Viene recuperato il valore esistente di "immutableString"
- "World" è aggiunto al valore esistente di "immutableString"
- Il valore risultante viene quindi assegnato a un nuovo blocco di memoria
- L'oggetto "immutableString" ora punta allo spazio di memoria appena creato
- Lo spazio di memoria creato in precedenza è ora disponibile per la garbage collection.
Il valore del tipo di stringa è immutabile, ma l'oggetto String, creato utilizzando il costruttore String (), è mutabile, poiché è un oggetto ed è possibile aggiungere nuove proprietà ad esso.
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
Nel frattempo, sebbene sia possibile aggiungere nuove proprietà, non è possibile modificare le proprietà già esistenti
Uno screenshot di un test nella console di Chrome
In conclusione, 1. tutto il valore del tipo di stringa (tipo primitivo) è immutabile. 2. L'oggetto String è modificabile, ma il valore del tipo di stringa (tipo primitivo) che contiene è immutabile.
new String
genera un wrapper mutabile attorno a una stringa immutabile
String
oggetto (wrapper), il che significa che non è immutabile (per impostazione predefinita; come qualsiasi altro oggetto puoi chiamarlo Object.freeze
per renderlo immutabile). Ma un tipo di valore stringa primitivo, contenuto String
o meno in un wrapper di oggetti, è sempre immutabile.
Per quanto riguarda la tua domanda (nel tuo commento alla risposta di Ash) sul StringBuilder in ASP.NET Ajax, gli esperti sembrano non essere d'accordo su questo.
Christian Wenz dice nel suo libro Programmazione ASP.NET AJAX (O'Reilly) che "questo approccio non ha alcun effetto misurabile sulla memoria (in effetti, l'implementazione sembra essere un tick più lento rispetto all'approccio standard)".
D'altro canto, Gallo e altri dicono nel loro libro ASP.NET AJAX in azione (Manning) che "Quando il numero di stringhe da concatenare è maggiore, il generatore di stringhe diventa un oggetto essenziale per evitare enormi cali di prestazioni".
Immagino che dovresti fare il tuo benchmarking e anche i risultati potrebbero differire tra i browser. Tuttavia, anche se non migliora le prestazioni, potrebbe comunque essere considerato "utile" per i programmatori che sono abituati a programmare con StringBuilders in linguaggi come C # o Java.
È un post in ritardo, ma non ho trovato una buona citazione del libro tra le risposte.
Ecco un preciso tranne che da un libro affidabile:
Le stringhe sono immutabili in ECMAScript, il che significa che una volta create, i loro valori non possono cambiare. Per modificare la stringa detenuta da una variabile, la stringa originale deve essere distrutta e la variabile deve essere riempita con un'altra stringa contenente un nuovo valore ... — JavaScript professionale per sviluppatori Web, 3a edizione, p.43
Ora, la risposta che cita l'estratto del libro di Rhino ha ragione sull'immutabilità delle stringhe, ma erroneamente dice "Le stringhe sono assegnate per riferimento, non per valore". (probabilmente inizialmente intendevano mettere le parole in modo opposto).
L'idea sbagliata "riferimento / valore" è chiarita nel "JavaScript professionale", capitolo denominato "Valori primitivi e di riferimento":
I cinque tipi primitivi ... [are]: Undefined, Null, Boolean, Number e String. Si dice che queste variabili siano accessibili per valore, perché si sta manipolando il valore effettivo memorizzato nella variabile. — JavaScript professionale per sviluppatori Web, 3a edizione, p.85
che si contrappone agli oggetti :
Quando manipoli un oggetto, stai davvero lavorando a un riferimento a quell'oggetto piuttosto che all'oggetto reale stesso. Per questo motivo, si dice che tali valori sono accessibili per riferimento. — JavaScript professionale per sviluppatori Web, 3a edizione, p.85
Le stringhe JavaScript sono davvero immutabili.
Le stringhe in Javascript sono immutabili