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

Tiré de MDN

Les littératures de chaînes (désignées par des guillemets doubles ou simples) et les chaînes renvoyées à partir d'appels de chaîne dans un contexte non constructeur (c.-à-d. Sans utiliser le nouveau mot-clé) sont des chaînes primitives. JavaScript convertit automatiquement les primitives en objets String, de sorte qu'il est possible d'utiliser des méthodes d'objet String pour les chaînes primitives. Dans les contextes où une méthode doit être invoquée sur une chaîne primitive ou une recherche de propriété se produit, JavaScript va automatiquement enrouler la primitive de chaîne et appeler la méthode ou effectuer la recherche de propriété.

Donc, j'ai pensé que les opérations (logiques) (les appels de méthodes) sur les littéralités de chaîne devraient être plus lentes que les opérations sur des objets de chaîne car tout litre de chaîne est converti en chaîne Objet (travail supplémentaire) avant la method appliquée sur la chaîne.

Mais dans ce cas d'essai , le résultat est opposé. Le code block-1 fonctionne plus rapidement que le code block-2 , les deux blocs de code sont donnés ci-dessous:

Code block-1:

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

Code bloc-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 code block-1 est toujours plus rapide. Quelqu'un peut-il expliquer cela, pourquoi le code block-1 est plus rapide que le code block-2 .

    JavaScript a deux catégories principales, des primivites et des objets.

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

    Les schémas de devis simple / double devis sont identiques en termes de fonctionnalité. En dehors de cela, le comportement que vous essayez de nommer s'appelle auto-boxing. Donc, ce qui se passe réellement, c'est qu'un primitif est converti en son type d'enveloppe lorsqu'une méthode du type wrapper est invoquée. Mettez simple:

     var s = 'test'; 

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

    Alors, qu'est-ce qui se passe lorsque vous faites le s.charAt(i) par exemple?

    Étant donné que s n'est pas une instance de String , JavaScript typeof string automatiquement s , qui a typeof string à son type de wrapper, String , avec typeof object ou plus précisément s.valueOf(s).prototype.toString.call = [object String] .

    Le comportement de l'auto-boxing se déplace de l'avant vers le type d'enveloppe, mais les opérations standard sont incroyablement rapides puisque vous utilisez un type de données plus simple. Cependant, l'auto-boxe et Object.prototype.valueOf ont des effets différents.

    Si vous souhaitez forcer l'auto-boxing ou jeter une primitive dans son type de wrapper, vous pouvez utiliser Object.prototype.valueOf , mais le comportement est différent. Sur la base d'une grande variété de scénarios de tests, l'auto-boxing n'applique que les méthodes «requises», sans altérer la nature primitive de la variable. C'est pourquoi vous obtenez une meilleure vitesse.

    Cela dépend plutôt de la mise en œuvre, mais je vais prendre un coup de feu. Je vais illustrer avec V8, mais je suppose que d'autres moteurs utilisent des approches similaires.

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

    Un objet String, d'autre part, est analysé dans un v8::StringObject qui étend l' Object et, en plus d'être un objet à part entière, sert d'enveloppe pour v8::String .

    Maintenant, il est logique, un appel à une new String('').method() doit supprimer cette v8::StringObject 's v8::String avant d'exécuter la méthode, donc elle est plus lente.


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

    La façon dont MDN le met semble être la manière la plus simple d'expliquer comment fonctionne la box-box des primitives (comme mentionné 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 en objet String chaque fois que vous devez appeler une méthode. Ceci est également mentionné de manière informative dans la spécification ES5 annotée. En ce qui concerne la résolution des propriétés (et "méthodes" ¹) des valeurs primitives:

    REMARQUE 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. […]

    À un niveau très bas, les chaînes sont les plus souvent implémentées comme des valeurs scalaires immuables. Exemple de structure de l'enveloppe:

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

    Plus vous venez de la primitive, plus il faudra pour y arriver. Dans la pratique, les primitives de String sont beaucoup plus fréquentes que StringObject s, donc il n'est pas une surprise pour les moteurs d'ajouter des méthodes à la classe d'objets correspondants (interprétés) des primitives de chaîne au lieu de se convertir entre String et StringObject car l'explication de MDN suggère .


    ¹ En JavaScript, "méthode" est juste une convention de dénomination pour une propriété qui résout une valeur de type de fonction.

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

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

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

    String Literal vs String Object

    Littérature de chaîne: les littéraux de chaînes sont immuables, ce qui signifie qu'une fois qu'ils sont créés, leur état ne peut pas être modifié, ce qui les rend également sécurisés.

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

    a==b résultat sera 'vrai' à la fois string se référer au même objet

    String Objet:

    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 résultat a==b sera faux, car ils ont des références différentes.

    Cheerios !!

    Lorsque vous déclarez:

     var s = '0123456789'; 

    Vous créez une primitive de chaîne. Cette primitive de chaîne possède 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 ne doit pas être converti en un objet. Le primitif lui-même peut invoquer les méthodes.

    La conversion en un objet complet (qui vous permet d'ajouter de nouvelles propriétés) est une étape supplémentaire et ne rend pas les courbes de chaînes plus rapides (en fait, votre test montre qu'il les rend plus lent).

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

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

    Alors que, dans le cas de String Object, nous pouvons assigner des propriétés

     var x = new String("hello"); xy = "world"; console.log(xy); // this will print world 

    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 fonctionnels pour cela. Donc, 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 de plus lorsque la méthode charAt (int) est également recherchée sur l'objet lui-même avant que le comportement standard ne commence (comme ci-dessus). Apparemment, il y a une quantité étonnamment importante de travail dans ce cas.

    BTW Je ne pense pas que les primitives sont réellement converties en Objets, mais le moteur de script marque simplement cette variable en tant que type de chaîne et, par conséquent, il peut trouver toutes les fonctions fournies pour cela, il semble que vous invoquiez un objet. N'oubliez pas qu'il s'agit d'un script qui fonctionne selon des principes différents d'un temps d'exécution OO.