Quelle est la différence entre les littéraux de chaîne et les objets de chaîne en JavaScript?

Prises à partir de MDN

Littéraux de chaîne (notés par des guillemets doubles ou simples) et chaînes renvoyé à partir D'appels de chaîne dans un contexte non-constructeur (c'est-à-dire sans en utilisant le nouveau mot-clé) sont des chaînes primitives. JavaScript automatiquement convertit les primitives en objets String, de sorte qu'il est possible d'utiliser Méthodes d'objet String pour les chaînes primitives. Dans des contextes où un méthode doit être invoquée sur une chaîne primitive ou une recherche de propriété se produit, JavaScript va envelopper automatiquement la chaîne primitive et appelez la méthode ou effectuez la recherche de propriété.

Donc, je pensais que (logiquement) les opérations (appels de méthode) sur les littéraux de chaîne devraient être plus lentes que les opérations sur les objets string car tout littéral de chaîne est converti en objet string (travail supplémentaire) avant que method ne soit appliqué sur la chaîne.

Mais dans ce cas de test , le résultat est opposé. Le bloc de code - 1 s'exécute plus rapidement que le bloc de code - 2 , les deux blocs de code sont donnés ci-dessous:

Bloc de Code-1 :

var s = '0123456789';
for (var i = 0; i < s.length; i++) {
  s.charAt(i);
}

Bloc de Code-2 :

var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
    s.charAt(i);
}

Les résultats varient dans les navigateurs mais le bloc de code -1 est toujours plus rapide. Quelqu'un peut-il m'expliquer pourquoi, pourquoi la bloc de code-1 est plus rapide que bloc de code-2.

87
demandé sur Daniel A. White 2013-06-23 03:08:33

8 réponses

JavaScript a deux catégories de types principales, les primivites et les objets.

var s = 'test';
var ss = new String('test');

Les modèles de guillemets simples/guillemets doubles sont identiques en termes de fonctionnalité. Cela mis à part, le comportement que vous essayez de nommer est appelé auto-boxe. Donc ce qui se passe réellement est qu'une primitive est converti son emballage type de méthode de l'emballage type est invoquée. Mettre simple:

var s = 'test';

Est un type de données primitif. Il n'a pas de méthodes, ce n'est rien de plus qu'un pointeur vers une donnée brute référence de mémoire, ce qui explique la vitesse d'accès aléatoire beaucoup plus rapide.

Alors que se passe-t-il quand vous faites s.charAt(i) par exemple?

Depuis s n'est pas une instance de String, JavaScript, auto-box s qui a typeof string pour son emballage de type, String, avec typeof object, ou plus précisément s.valueOf(s).prototype.toString.call = [object String].

Le comportement d'auto-boxe convertit s en arrière vers son type de wrapper selon les besoins, mais les opérations standard sont incroyablement rapides puisque vous avez affaire à un type de données plus simple. Cependant auto-boxe et Object.prototype.valueOf ont des effets différents.

Si vous voulez forcer l'auto-boxing ou de jeter une primitive de son emballage, vous pouvez utiliser Object.prototype.valueOf, mais le comportement est différent. Basé sur une grande variété de scénarios de test, l'auto-boxe n'applique que les méthodes "requises", sans modifier la nature primitive de la variable. C'est pourquoi vous obtenez une meilleure vitesse.

108
répondu flavian 2013-06-22 23:34:19

C'est plutôt dépendant de l'implémentation, mais je vais essayer. Je vais illustrer avec V8 mais je suppose que d'autres moteurs utilisent des approches similaires.

Une primitive de chaîne est analysée en v8::String objet. Par conséquent, les méthodes peuvent être invoquées directement dessus comme mentionné par jfriend00 .

Un objet String, en revanche, est analysé en v8::StringObject qui s'étend sur Object et, en plus d'être un objet à part entière, Sert de wrapper pour v8::String.

Maintenant c'est seulement logique, un appel à new String('').method() doit déballer ce v8::StringObject s v8::String avant d'exécuter la méthode, donc c'est plus lent.


Dans beaucoup d'autres langues, les valeurs primitives n'ont pas de méthodes.

La façon dont MDN le met semble être le moyen le plus simple d'expliquer comment fonctionne l'auto-boxe des primitives (comme mentionné également dans la réponse de flav), c'est-à-dire comment les valeurs primitives-y de JavaScript peuvent invoquer des méthodes.

Cependant, un moteur intelligent ne convertira pas une chaîne primitive-y pour objet de Chaîne chaque fois que vous devez à appeler une méthode. Ceci est également mentionné informativement dans la spécification ES5 annotée .{[16] } en ce qui concerne la résolution des propriétés (et"méthodes" 1) des valeurs primitives:

NOTE L'objet qui peut être créé à l'étape 1 n'est pas accessible en dehors de la méthode ci-dessus. Une implémentation peut choisir d'éviter la création réelle de l'objet. [...]

Au niveau très bas, les chaînes sont le plus souvent implémenté en tant que valeurs scalaires immuables. Exemple de structure d'emballage:

StringObject > String (> ...) > char[]

Plus vous êtes loin du primitif, plus il faudra de temps pour y arriver. En pratique, les String primitives sont beaucoup plus fréquentes que les StringObjects, il n'est donc pas surprenant que les moteurs ajoutent des méthodes à la classe d'objets correspondants (interprétés) des primitives de chaîne au lieu de convertir entre String et StringObject comme le suggère L'explication de MDN.


1 en JavaScript," méthode " est juste une convention de nommage pour une propriété qui se résout en une valeur de type fonction.

27
répondu Fabrício Matté 2015-04-12 23:45:17

Dans le cas d'un littéral de chaîne, nous ne pouvons pas attribuer de propriétés

var x = "hello" ;
x.y = "world";
console.log(x.y); // this will print undefined

Alors que dans le cas D'un objet String, nous pouvons attribuer des propriétés

var x = new String("hello");
x.y = "world";
console.log(x.y); // this will print world
10
répondu refactor 2016-04-14 06:01:15

Si vous utilisez new, vous indiquez explicitement que vous souhaitez créer une instance d'un objet . Par conséquent, new String produit un objet enveloppant la primitive String , ce qui signifie que toute action implique une couche de travail supplémentaire.

typeof new String(); // "object"
typeof '';           // "string"

, Comme ils sont de différents types, vos JavaScript interprète peut également optimiser différemment, , comme mentionné dans les commentaires.

9
répondu Paul S. 2017-05-23 11:33:13

Chaîne De Caractères Littérale:

Les littéraux de chaîne sont immuables, ce qui signifie qu'une fois créés, leur état ne peut pas être modifié, ce qui les rend également sûrs.

var a = 's';
var b = 's';

a==b le résultat sera 'true' les deux chaînes font référence au même objet.

Objet Chaîne:

Ici, deux objets différents sont créés, et ils ont des références différentes:

var a = new String("s");
var b = new String("s");

a==b le résultat sera faux, car ils ont des références différentes.

9
répondu Wajahat Ali Qureshi 2017-08-21 15:14:01

Lorsque vous déclarez:

var s = '0123456789';

Vous créez une primitive de chaîne. Cette primitive de chaîne a des méthodes qui vous permettent d'appeler des méthodes sans convertir la primitive en un objet de première classe. Donc, votre supposition que ce serait plus lent parce que la chaîne doit être convertie en un objet n'est pas correcte. Il n'a pas à être converti en un objet. La primitive elle-même peut invoquer les méthodes.

Le convertir en un objet complet (ce qui vous permet d'ajouter de nouvelles propriétés pour cela) est une étape supplémentaire et ne rend pas les oeprations de chaîne plus rapides (en fait, votre test montre qu'il les rend plus lents).

5
répondu jfriend00 2013-06-22 23:15:14

L'existence d'un objet a peu à voir avec le comportement réel d'une chaîne dans les moteurs ECMAScript/JavaScript car la portée racine contiendra simplement des objets de fonction pour cela. Ainsi, la fonction charAt (int) dans le cas d'un littéral de chaîne sera recherchée et exécutée.

Avec un objet réel, vous ajoutez une couche supplémentaire où la méthode charAt (int) est également recherchée sur l'objet lui-même avant que le comportement standard n'entre en jeu (comme ci-dessus). Apparemment il y a une quantité étonnamment grande de travail effectué dans ce cas.

BTW Je ne pense pas que les primitives soient réellement converties en objets, mais le moteur de script marquera simplement cette variable comme type de chaîne et pourra donc trouver toutes les fonctions fournies pour cela, donc il semble que vous invoquiez un objet. N'oubliez pas qu'il s'agit d'un runtime de script qui fonctionne sur des principes différents d'un runtime OO.

3
répondu clearwater 2013-06-22 23:30:13

Je peux voir que cette question a été résolue il y a longtemps, il y a une autre distinction subtile entre les littéraux de chaîne et les objets de chaîne, car personne ne semble y avoir touché, je pensais que je l'écrirais juste pour l'exhaustivité.

Fondamentalement, une autre distinction entre les deux est lors de l'utilisation d'eval. eval ('1 + 1') donne 2, alors que eval (new String ('1 + 1')) donne '1 + 1', donc si certains blocs de code peuvent être exécutés à la fois 'normalement' ou avec eval, cela pourrait conduire à des résultats étranges

2
répondu luanped 2018-06-03 09:18:15