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, sliceretornam 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.joinArray.push , em seguida, usandoArray.joinEntão eu criei os mesmos três testes abstraindo-los em StringBuilderConcat, StringBuilderArrayPushe 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.joinsó 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.
joinconcatenaçã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 joinenumera a matriz internamente, portanto, não é trapaça omitir um forloop do jointeste).
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 numbere 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);
Stringobjetos 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 Numberobjetos e valores numéricos. É importante observar que os Stringobjetos criados usando new Stringnã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á StringBuilderno 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 Stringgera um invólucro mutável em torno de uma seqüência imutável
Stringobjeto (invólucro), o que significa que não é imutável (por padrão; como qualquer outro objeto que você pode chamar Object.freezepara torná-lo imutável). Mas um tipo de valor de cadeia primitivo, contido Stringou 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