O javascript usa seqüências imutáveis ou mutáveis? Preciso de um "construtor de strings"?
O javascript usa seqüências imutáveis ou mutáveis? Preciso de um "construtor de strings"?
Respostas:
Eles são imutáveis. Você não pode alterar um caractere dentro de uma string com algo parecido var myString = "abbdef"; myString[2] = 'c'
. Os métodos de manipulação de strings, como trim
, slice
retornam novas strings.
Da mesma forma, se você tiver duas referências à mesma sequência, modificar uma não afeta a outra
let a = b = "hello";
a = a + " world";
// b is not affected
No entanto, sempre ouvi o que Ash mencionou em sua resposta (que usar Array.join é mais rápido para concatenação), então eu queria testar os diferentes métodos de concatenar seqüências de caracteres e abstrair a maneira mais rápida em um StringBuilder. Eu escrevi alguns testes para ver se isso é verdade (não é!).
Era o que eu achava que seria a maneira mais rápida, embora eu continuasse pensando que adicionar uma chamada de método pode torná-la mais lenta ...
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('');
}
Aqui estão os testes de velocidade de desempenho. Todos os três criam uma cadeia gigantesca composta de concatenação"Hello diggity dog"
cem mil vezes em uma cadeia vazia.
Eu criei três tipos de testes
Array.push
eArray.join
Array.push
, em seguida, usandoArray.join
Então eu criei os mesmos três testes abstraindo-los em StringBuilderConcat
, StringBuilderArrayPush
e StringBuilderArrayIndex
http://jsperf.com/string-concat-without-sringbuilder/5 Por favor, vá lá e executar testes para que possamos obter uma amostra agradável. Observe que eu corrigi um pequeno bug; portanto, os dados dos testes foram apagados; atualizarei a tabela assim que houver dados de desempenho suficientes. Acesse http://jsperf.com/string-concat-without-sringbuilder/5 para obter a tabela de dados antiga.
Aqui estão alguns números (última atualização no Ma5rch 2018), se você não quiser seguir o link. O número em cada teste é de 1000 operações / segundo (quanto maior, melhor )
| 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 |
Constatações
Atualmente, todos os navegadores sempre verdes lidam bem com a concatenação de strings. Array.join
só ajuda o IE 11
No geral, o Opera é mais rápido, 4 vezes mais rápido que o Array.join
O Firefox é o segundo e Array.join
é apenas um pouco mais lento no FF, mas consideravelmente mais lento (3x) no Chrome.
O Chrome é o terceiro, mas o concat de strings é 3 vezes mais rápido que o Array.join
Criar um StringBuilder parece não afetar muito o desempenho.
Espero que alguém ache isso útil
Caso de teste diferente
Como o @RoyTinker achou que meu teste era defeituoso, criei um novo caso que não cria uma grande string concatenando a mesma string, ele usa um caractere diferente para cada iteração. A concatenação de cordas ainda parecia mais rápida ou mais rápida. Vamos executar esses testes.
Sugiro que todos continuem pensando em outras maneiras de testar isso e fique à vontade para adicionar novos links para diferentes casos de teste abaixo.
join
concatenação de vs strings, criando assim a matriz antes do teste. Eu não acho que isso é trapaça se esse objetivo for entendido (e join
enumera a matriz internamente, portanto, não é trapaça omitir um for
loop do join
teste).
Array.join
do livro rinoceronte :
No JavaScript, as strings são objetos imutáveis, o que significa que os caracteres dentro deles não podem ser alterados e que quaisquer operações nas strings realmente criam novas strings. As strings são atribuídas por referência, não por valor. Em geral, quando um objeto é atribuído por referência, uma alteração feita no objeto por meio de uma referência fica visível em todas as outras referências ao objeto. Como as seqüências de caracteres não podem ser alteradas, no entanto, você pode ter várias referências a um objeto de sequência e não se preocupar que o valor da sequência seja alterado sem que você saiba
null
undefined
number
e boolean
. As strings são atribuídas por valor e não por referência e são passadas como tal. Assim, as strings não são apenas imutáveis, elas são um valor . Mudar a string "hello"
para ser "world"
é como decidir que a partir de agora o número 3 é o número 4 ... não faz sentido.
var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
String
objetos criados usando o construtor de strings são wrappers em torno dos valores de strings JavaScript. Você pode acessar o valor da sequência do tipo em caixa usando a .valueOf()
função - isso também é válido para Number
objetos e valores numéricos. É importante observar que os String
objetos criados usando new String
não são cadeias reais, mas são invólucros ou caixas em torno das cadeias. Consulte es5.github.io/#x15.5.2.1 . Sobre como as coisas converter em objetos ver es5.github.io/#x9.9
Dica de desempenho:
Se você precisar concatenar cadeias grandes, coloque as partes em uma matriz e use o Array.Join()
método para obter a cadeia geral. Isso pode ser muitas vezes mais rápido para concatenar um grande número de strings.
Não há StringBuilder
no JavaScript.
Apenas para esclarecer mentes simples como a minha (da MDN ):
Imutáveis são os objetos cujo estado não pode ser alterado depois que o objeto é criado.
String e Numbers são imutáveis.
Imutável significa que:
Você pode fazer um nome de variável apontar para um novo valor, mas o valor anterior ainda é mantido na memória. Daí a necessidade de coleta de lixo.
var immutableString = "Hello";
// No código acima, um novo objeto com o valor da string é criado.
immutableString = immutableString + "World";
// Agora estamos anexando "Mundo" ao valor existente.
Parece que estamos mudando a string 'immutableString', mas não estamos. Em vez de:
Ao anexar o "immutableString" com um valor de sequência, ocorrem os seguintes eventos:
- O valor existente de "immutableString" é recuperado
- "Mundo" é anexado ao valor existente de "immutableString"
- O valor resultante é então alocado para um novo bloco de memória
- O objeto "immutableString" agora aponta para o espaço de memória recém-criado
- O espaço de memória criado anteriormente está agora disponível para coleta de lixo.
O valor do tipo string é imutável, mas o objeto String, criado usando o construtor String (), é mutável, porque é um objeto e você pode adicionar novas propriedades.
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
Enquanto isso, embora você possa adicionar novas propriedades, não é possível alterar as propriedades já existentes.
Captura de tela de um teste no console do Chrome
Em conclusão, 1. todo o valor do tipo string (tipo primitivo) é imutável. 2. O objeto String é mutável, mas o valor do tipo de string (tipo primitivo) que ele contém é imutável.
new String
gera um invólucro mutável em torno de uma seqüência imutável
String
objeto (invólucro), o que significa que não é imutável (por padrão; como qualquer outro objeto que você pode chamar Object.freeze
para torná-lo imutável). Mas um tipo de valor de cadeia primitivo, contido String
ou não em um invólucro de objeto, é sempre imutável.
Em relação à sua pergunta (no seu comentário à resposta de Ash) sobre o StringBuilder no ASP.NET Ajax, os especialistas parecem discordar.
Christian Wenz diz em seu livro Programming ASP.NET AJAX (O'Reilly) que "essa abordagem não tem nenhum efeito mensurável na memória (de fato, a implementação parece ser um tique mais lento que a abordagem padrão)".
Por outro lado, Gallo e colaboradores dizem em seu livro ASP.NET AJAX in Action (Manning) que "quando o número de seqüências de caracteres para concatenar é maior, o construtor de seqüências de caracteres se torna um objeto essencial para evitar grandes quedas de desempenho".
Acho que você precisaria fazer seu próprio benchmarking e os resultados também podem diferir entre os navegadores. No entanto, mesmo que não melhore o desempenho, ainda pode ser considerado "útil" para programadores acostumados a codificar com StringBuilders em linguagens como C # ou Java.
É um post tardio, mas não encontrei uma boa citação de livro entre as respostas.
Aqui está uma definição, exceto em um livro confiável:
Strings são imutáveis no ECMAScript, o que significa que, uma vez criados, seus valores não podem ser alterados. Para alterar a string mantida por uma variável, a string original deve ser destruída e a variável preenchida com outra string contendo um novo valor ... - JavaScript profissional para desenvolvedores web, 3ª Ed., P.43
Agora, a resposta que cita o trecho do livro Rhino está certa sobre imutabilidade de strings, mas está errado dizer "Strings são atribuídos por referência, não por valor". (provavelmente eles originalmente pretendiam colocar as palavras de maneira oposta).
O equívoco "referência / valor" é esclarecido no capítulo "JavaScript profissional", chamado "Valores primitivos e de referência":
Os cinco tipos primitivos ... [são]: Indefinido, Nulo, Booleano, Número e String. Diz-se que essas variáveis são acessadas por valor, porque você está manipulando o valor real armazenado na variável. - JavaScript profissional para desenvolvedores da Web, 3ª Ed., P.85
isso é contra objetos :
Quando você manipula um objeto, está realmente trabalhando em uma referência a esse objeto, e não no próprio objeto. Por esse motivo, diz-se que esses valores são acessados por referência. - JavaScript profissional para desenvolvedores da Web, 3ª Ed., P.85
As strings JavaScript são realmente imutáveis.
Strings em Javascript são imutáveis