Retirado do MDN
Literais de string (denotados por aspas duplas ou simples) e strings retornadas de chamadas de String em um contexto não construtor (ou seja, sem usar a nova palavra-chave) são strings primitivas. JavaScript converte automaticamente primitivos em objetos String, de forma que seja possível usar métodos de objeto String para strings primitivas. Em contextos em que um método deve ser invocado em uma string primitiva ou ocorre uma pesquisa de propriedade, o JavaScript encapsula automaticamente a primitiva de string e chama o método ou executa a pesquisa de propriedade.
Portanto, pensei (logicamente) que as operações (chamadas de método) em primitivos de string deveriam ser mais lentas do que as operações em Objetos de string porque qualquer primitivo de string é convertido em Object de string (trabalho extra) antes de method
ser aplicado na string.
Mas, neste caso de teste , o resultado é o oposto. O bloco de código 1 é executado mais rápido do que o bloco de código 2 , ambos os blocos de código são fornecidos abaixo:
bloco de código-1:
var s = '0123456789';
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
bloco de código 2:
var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
Os resultados variam em navegadores, mas o bloco de código-1 é sempre mais rápido. Alguém pode explicar por que o bloco de código 1 é mais rápido do que o bloco 2 .
fonte
new String
introduz outra camada transparente de quebra automática de objeto .typeof new String(); //"object"
'0123456789'.charAt(i)
?code block-1
é mais rápido?Respostas:
JavaScript tem duas categorias de tipo principais, primivites e objetos.
Os padrões de aspas simples / aspas duplas são idênticos em termos de funcionalidade. Deixando isso de lado, o comportamento que você está tentando nomear é chamado de boxe automático. Portanto, o que realmente acontece é que uma primitiva é convertida em seu tipo de invólucro quando um método do tipo de invólucro é invocado. Simplificando:
É um tipo de dados primitivo. Não tem métodos, nada mais é do que um ponteiro para uma referência de memória de dados brutos, o que explica a velocidade de acesso aleatório muito mais rápida.
Então, o que acontece quando você faz isso,
s.charAt(i)
por exemplo?Uma vez que
s
não é uma instância deString
, o JavaScript fará uma caixa automáticas
, que temtypeof string
seu tipo de invólucroString
, comtypeof object
ou mais precisamentes.valueOf(s).prototype.toString.call = [object String]
.O comportamento de caixa automática é convertido
s
para frente e para trás em seu tipo de invólucro conforme necessário, mas as operações padrão são incrivelmente rápidas, pois você está lidando com um tipo de dados mais simples. No entanto, auto-boxing eObject.prototype.valueOf
tem efeitos diferentes.Se você quiser forçar a caixa automática ou converter uma primitiva para seu tipo de invólucro, você pode usar
Object.prototype.valueOf
, mas o comportamento é diferente. Com base em uma ampla variedade de cenários de teste, a caixa automática aplica apenas os métodos "necessários", sem alterar a natureza primitiva da variável. É por isso que você obtém mais velocidade.fonte
Isso depende da implementação, mas vou tentar. Vou exemplificar com o V8, mas presumo que outros motores usam abordagens semelhantes.
Uma primitiva de string é analisada em um
v8::String
objeto. Conseqüentemente, os métodos podem ser chamados diretamente nele, conforme mencionado por jfriend00 .Um objeto String, por outro lado, é analisado em um
v8::StringObject
que se estendeObject
e, além de ser um objeto completo, serve como um invólucro parav8::String
.Agora é lógico, uma chamada para
new String('').method()
tem que unbox estev8::StringObject
év8::String
antes de executar o método, por isso é mais lento.Em muitas outras linguagens, os valores primitivos não têm métodos.
A forma como o MDN coloca parece ser a maneira mais simples de explicar como funciona a caixa automática dos primitivos (como também mencionado na resposta de flav ), ou seja, como os valores de y primitivo do JavaScript podem invocar métodos.
No entanto, um mecanismo inteligente não converterá uma string primitiva-y em um objeto String toda vez que você precisar chamar um método. Isso também é mencionado informativamente nas especificações ES5 anotadas. com relação à resolução de propriedades (e "métodos" ¹) de valores primitivos:
Em um nível muito baixo, Strings são mais frequentemente implementadas como valores escalares imutáveis. Exemplo de estrutura de wrapper:
Quanto mais longe você estiver do primitivo, mais tempo levará para chegar a ele. Na prática, os
String
primitivos são muito mais frequentes do queStringObject
s, portanto, não é uma surpresa para os mecanismos adicionarem métodos à classe dos objetos correspondentes (interpretados) dos primitivos String, em vez de fazer a conversão entreString
eStringObject
como sugere a explicação do MDN.¹ Em JavaScript, "método" é apenas uma convenção de nomenclatura para uma propriedade que é resolvida como um valor da função de tipo.
fonte
=]
Agora estou me perguntando se a explicação do MDN está lá apenas porque parece ser a maneira mais fácil de entender a caixa automática ou se há alguma referência a ela na especificação ES. Lendo toda a especificação no momento para verificar, lembre-se de atualizo a resposta se eu encontrar uma referência.No caso de literal de string, não podemos atribuir propriedades
Considerando que, no caso de String Object, podemos atribuir propriedades
fonte
String
objetos também. Obrigado!Literal de string:
Literais de string são imutáveis, o que significa que, uma vez criados, seu estado não pode ser alterado, o que também os torna thread-safe.
a==b
o resultado será 'verdadeiro' ambas as strings referem-se ao mesmo objeto.Objeto de string:
Aqui, dois objetos diferentes são criados e têm referências diferentes:
a==b
o resultado será falso, porque eles têm referências diferentes.fonte
a
&b
tentar atribuira[0] = 'X'
ele vai ser executado com êxito, mas não vai funcionar como você poderia esperarSe você usar
new
, estará declarando explicitamente que deseja criar uma instância de um objeto . Portanto,new String
está produzindo um objeto envolvendo a primitiva String , o que significa que qualquer ação sobre ele envolve uma camada extra de trabalho.Como são de tipos diferentes, seu interpretador de JavaScript também pode otimizá-los de maneira diferente, conforme mencionado nos comentários .
fonte
Quando você declara:
você cria um primitivo de string. Essa primitiva de string tem métodos que permitem chamar métodos nela sem converter a primitiva em um objeto de primeira classe. Portanto, sua suposição de que isso seria mais lento porque a string deve ser convertida em um objeto não está correta. Ele não precisa ser convertido em um objeto. O próprio primitivo pode invocar os métodos.
Convertê-lo em um objeto totalmente desenvolvido (que permite adicionar novas propriedades a ele) é uma etapa extra e não torna as operações de string mais rápidas (na verdade, seu teste mostra que isso as torna mais lentas).
fonte
String.prototype
?var s = '0123456789';
é um valor primitivo, como pode esse valor ter métodos, estou confuso!Eu posso ver que esta questão foi resolvida há muito tempo, há outra distinção sutil entre literais de string e objetos de string, como ninguém parece ter tocado nisso, pensei em escrever apenas para completar.
Basicamente, outra distinção entre os dois é quando se usa eval. eval ('1 + 1') dá 2, enquanto eval (new String ('1 + 1')) dá '1 + 1', então se determinado bloco de código pode ser executado tanto 'normalmente' como com eval, ele poderia levar a resultados estranhos
fonte
new String("")
retorna um objeto, e eval avalia apenas a string, e retorna todas as coisas como estãoA existência de um objeto tem pouco a ver com o comportamento real de uma String nos mecanismos ECMAScript / JavaScript, pois o escopo raiz simplesmente conterá objetos de função para isso. Assim, a função charAt (int) no caso de uma string literal será pesquisada e executada.
Com um objeto real, você adiciona mais uma camada onde o método charAt (int) também é pesquisado no próprio objeto antes que o comportamento padrão seja ativado (o mesmo que acima). Aparentemente, há uma quantidade surpreendentemente grande de trabalho realizada neste caso.
BTW, não acho que os primitivos sejam realmente convertidos em objetos, mas o mecanismo de script simplesmente marcará essa variável como tipo de string e, portanto, pode encontrar todas as funções fornecidas para que pareça que você invoca um objeto. Não se esqueça que este é um runtime de script que funciona em princípios diferentes de um runtime OO.
fonte
A maior diferença entre uma primitiva de string e um objeto de string é que os objetos devem seguir esta regra para o
==
operador :Portanto, embora os primitivos de string tenham uma conveniência
==
que compara o valor, você está sem sorte quando se trata de fazer qualquer outro tipo de objeto imutável (incluindo um objeto de string) se comportar como um tipo de valor.(Outros notaram que um objeto de string é tecnicamente mutável porque você pode adicionar propriedades a ele. Mas não está claro para que isso é útil; o valor da string em si não é mutável.)
fonte
O código é otimizado antes de ser executado pelo mecanismo javascript. Em geral, micro benchmarks podem ser enganosos porque compiladores e interpretadores reorganizam, modificam, removem e executam outros truques em partes de seu código para torná-lo executado mais rápido. Em outras palavras, o código escrito informa qual é o objetivo, mas o compilador e / ou o tempo de execução decidirá como atingir esse objetivo.
O bloco 1 é mais rápido principalmente por causa de: var s = '0123456789'; é sempre mais rápido do que var s = new String ('0123456789'); por causa da sobrecarga da criação do objeto.
A parte do loop não é a causa da desaceleração, porque chartAt () pode ser embutido pelo interpretador. Tente remover o loop e execute o teste novamente, você verá que a relação de velocidade será a mesma como se o loop não tivesse sido removido. Em outras palavras, para esses testes, os blocos de loop em tempo de execução possuem exatamente o mesmo bytecode / código de máquina.
Para esses tipos de micro benchmarks, olhar para o bytecode ou código de máquina fornecerá uma imagem mais clara.
fonte
Em Javascript, os tipos de dados primitivos, como string, são um bloco de construção não composto. Isso significa que eles são apenas valores, nada mais:
let a = "string value";
Por padrão, não há métodos embutidos como toUpperCase, toLowerCase etc ...Mas, se você tentar escrever:
Isso não gerará nenhum erro; em vez disso, eles funcionarão como deveriam.
O que aconteceu ? Bem, quando você tenta acessar uma propriedade de uma string, o
a
Javascript força a string a um objetonew String(a);
conhecido como objeto wrapper .Este processo está vinculado a conceitos chamados construtores de funções em Javascript, onde funções são utilizadas para criar novos objetos.
Quando você digita
new String('String value');
aqui String é o construtor de função, que recebe um argumento e cria um objeto vazio dentro do escopo da função, esse objeto vazio é atribuído a este e, neste caso, String fornece todas as funções internas conhecidas que mencionamos antes. e assim que a operação for concluída, por exemplo, fazer uma operação em maiúsculas, o objeto wrapper é descartado.Para provar isso, vamos fazer isso:
Aqui, a saída será indefinida. Por quê ? Nesse caso, o Javascript cria o objeto String do wrapper, define a nova propriedade addNewProperty e descarta o objeto do wrapper imediatamente. é por isso que você fica indefinido. O pseudocódigo ficaria assim:
fonte
podemos definir String de três maneiras
// também podemos criar usando 4. var d = a + '';
Verifique o tipo das strings criadas usando o operador typeof
quando você compara a e b var
a==b ( // yes)
quando você compara objeto String
fonte