Tiré de MDN
Les chaînes littérales (désignées par des guillemets doubles ou simples) et les chaînes renvoyées par les appels String dans un contexte non constructeur (c'est-à-dire 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 appelée sur une chaîne primitive ou une recherche de propriété se produit, JavaScript encapsule automatiquement la chaîne primitive et appelle la méthode ou effectue la recherche de propriété.
Donc, j'ai pensé (logiquement) que les opérations (appels de méthode) sur les primitives de chaîne devraient être plus lentes que les opérations sur les objets de chaîne car toute primitive de chaîne est convertie en objet de chaîne (travail supplémentaire) avant d' method
être appliquée 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 selon les navigateurs mais le code block-1 est toujours plus rapide. Quelqu'un peut-il expliquer ceci, pourquoi le bloc de code 1 est plus rapide que le bloc de code 2 .
la source
new String
introduit une autre couche transparente d' habillage d' objets .typeof new String(); //"object"
'0123456789'.charAt(i)
?code block-1
est plus rapide?Réponses:
JavaScript a deux catégories de types principales, les primivites et les objets.
Les modèles de guillemets simples / doubles sont identiques en termes de fonctionnalités. Cela mis à part, le comportement que vous essayez de nommer s'appelle la boxe automatique. Donc, ce qui se passe réellement, c'est qu'une primitive est convertie en son type wrapper lorsqu'une méthode du type wrapper est appelée. En termes simples:
Est un type de données primitif. Il n'a pas de méthode, ce n'est rien de plus qu'un pointeur vers une référence mémoire de données brutes, ce qui explique la vitesse d'accès aléatoire beaucoup plus rapide.
Alors, que se passe-t-il lorsque vous faites
s.charAt(i)
par exemple?Puisqu'il
s
n'est pas une instance deString
, JavaScript encapsulera automatiquements
, qui doittypeof string
son type de wrapper,,String
avectypeof object
ou plus préciséments.valueOf(s).prototype.toString.call = [object String]
.Le comportement de mise en boîte automatique effectue un va-
s
et-vient vers son type d'enveloppe selon les besoins, mais les opérations standard sont incroyablement rapides car vous avez affaire à un type de données plus simple. Cependant l'auto-boxe etObject.prototype.valueOf
ont des effets différents.Si vous souhaitez forcer l'auto-boxing ou convertir une primitive en son type d'enveloppe, vous pouvez utiliser
Object.prototype.valueOf
, mais le comportement est différent. Sur la base d'une grande variété de scénarios de test, 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.la source
Cela dépend plutôt de la mise en œuvre, 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 un
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 un
v8::StringObject
qui s'étendObject
et, en plus d'être un objet à part entière, sert de wrapper pourv8::String
.Maintenant , il est logique, un appel à
new String('').method()
doit unboxv8::StringObject
c'estv8::String
avant d' exécuter la méthode, il est donc plus lent.Dans de nombreux autres langages, les valeurs primitives n'ont pas de méthodes.
La façon dont MDN le dit semble être le moyen le plus simple d'expliquer comment fonctionne l'auto-boxing des primitives (comme également mentionné dans la réponse de flav ), c'est-à-dire comment le primitif-y de JavaScript valeurs 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 des "méthodes" ¹) des valeurs primitives:
À très bas niveau, les chaînes sont le plus souvent implémentées en tant que valeurs scalaires immuables. Exemple de structure de wrapper:
Plus vous êtes loin du primitif, plus il faudra de temps pour y arriver. En pratique, les
String
primitives sont beaucoup plus fréquentes queStringObject
s, il n'est donc pas surprenant que les moteurs ajoutent des méthodes à la classe des objets correspondants (interprétés) des primitives String au lieu de faire des va-et-vient entreString
etStringObject
comme le suggère l'explication de MDN.¹ En JavaScript, «méthode» est simplement une convention de dénomination pour une propriété qui se résout en une valeur de type fonction.
la source
=]
Maintenant, je me demande si l'explication de MDN est là simplement parce que cela semble être le moyen le plus simple de comprendre l'auto-boxing ou s'il y a une référence à celui-ci dans la spécification ES. mettre à jour la réponse si jamais je trouve une référence.En cas de littéral de chaîne, nous ne pouvons pas attribuer de propriétés
Alors que dans le cas de String Object, nous pouvons attribuer des propriétés
la source
String
objets. Je vous remercie!Chaîne littérale:
Les littéraux de chaîne 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ûrs pour les threads.
a==b
le résultat sera «vrai» le même objet pour les deux chaînes.Objet chaîne:
Ici, deux objets différents sont créés, et ils ont des références différentes:
a==b
Le résultat sera faux, car ils ont des références différentes.la source
a
etb
essayez de l'assigner,a[0] = 'X'
il sera exécuté avec succès mais ne fonctionnera pas comme vous pourriez vous y attendreSi vous utilisez
new
, vous indiquez explicitement que vous souhaitez créer une instance d'un Object . Par conséquent,new String
produit un objet encapsulant la primitive String , ce qui signifie que toute action sur celui-ci implique une couche supplémentaire de travail.Comme ils sont de types différents, votre interpréteur JavaScript peut également les optimiser différemment, comme mentionné dans les commentaires .
la source
Lorsque vous déclarez:
vous créez une chaîne primitive. Cette primitive de chaîne a des méthodes qui vous permettent d'appeler des méthodes dessus 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 objet n'est pas correcte. Il n'a pas besoin d'être converti en objet. La primitive elle-même peut invoquer les méthodes.
Le convertir en un objet à part entière (qui vous permet d'y ajouter de nouvelles propriétés) est une étape supplémentaire et ne rend pas les opérations de la chaîne plus rapides (en fait, votre test montre que cela les ralentit).
la source
String.prototype
?var s = '0123456789';
est une valeur primitive, comment cette valeur peut-elle avoir des méthodes, je suis confus!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, comme personne ne semble l'avoir touché, j'ai pensé que je l'écrirais juste pour être complet.
Fondamentalement, une autre distinction entre les deux est lors de l'utilisation de eval. eval ('1 + 1') donne 2, alors que eval (new String ('1 + 1')) donne '1 + 1', donc si un certain bloc de code peut être exécuté à la fois 'normalement' ou avec eval, il pourrait conduire à des résultats étranges
la source
new String("")
retourne un objet, et eval n'évalue que la chaîne, et retourne tout le reste tel quelL'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 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 un autre calque où la méthode charAt (int) est également recherchée sur l'objet lui-même avant que le comportement standard ne démarre (comme ci-dessus). Apparemment, il y a un travail étonnamment important 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 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 runtime de script qui fonctionne sur des principes différents de celui d'un runtime OO.
la source
La plus grande différence entre une chaîne primitive et un objet chaîne est que les objets doivent suivre cette règle pour l'
==
opérateur :Ainsi, alors que les primitives de chaîne ont une fonction pratique
==
qui compare la valeur, vous n'avez pas de chance quand il s'agit de faire en sorte que tout autre type d'objet immuable (y compris un objet de chaîne) se comporte comme un type valeur.(D'autres ont noté qu'un objet chaîne est techniquement modifiable car vous pouvez lui ajouter des propriétés. Mais on ne sait pas à quoi cela sert; la valeur de chaîne elle-même n'est pas modifiable.)
la source
Le code est optimisé avant d'être exécuté par le moteur javascript. En général, les micro-tests peuvent être trompeurs car les compilateurs et les interprètes réorganisent, modifient, suppriment et exécutent d'autres astuces sur des parties de votre code pour le rendre plus rapide. En d'autres termes, le code écrit indique quel est l'objectif, mais le compilateur et / ou le moteur d'exécution décideront comment atteindre cet objectif.
Le bloc 1 est plus rapide principalement à cause de: var s = '0123456789'; est toujours plus rapide que var s = new String ('0123456789'); en raison de la surcharge de la création d'objets.
La portion de boucle n'est pas celle qui cause le ralentissement car le chartAt () peut être inséré par l'interpréteur. Essayez de supprimer la boucle et relancez le test, vous verrez que le rapport de vitesse sera le même que si la boucle n'avait pas été supprimée. En d'autres termes, pour ces tests, les blocs de boucle au moment de l'exécution ont exactement le même bytecode / code machine.
Pour ces types de micro-benchmarks, regarder le bytecode ou le code machine fournira une image plus claire.
la source
En Javascript, les types de données primitifs tels que string sont un bloc de construction non composite. Cela signifie que ce ne sont que des valeurs, rien de plus:
let a = "string value";
par défaut, il n'y a pas de méthodes intégrées comme toUpperCase, toLowerCase etc ...Mais, si vous essayez d'écrire:
Cela ne générera aucune erreur, mais ils fonctionneront comme ils le devraient.
Qu'est-il arrivé ? Eh bien, lorsque vous essayez d'accéder à une propriété d'une chaîne,
a
Javascript contraint la chaîne à un objetnew String(a);
appelé objet wrapper .Ce processus est lié au concept appelé constructeurs de fonctions en Javascript, où les fonctions sont utilisées pour créer de nouveaux objets.
Lorsque vous tapez
new String('String value');
ici String est un constructeur de fonction, qui prend un argument et crée un objet vide à l'intérieur de la portée de la fonction, cet objet vide est affecté à ce et dans ce cas, String fournit toutes celles connues fonctions intégrées dont nous avons parlé auparavant. et dès que l'opération est terminée, par exemple faire une opération en majuscules, l'objet wrapper est rejeté.Pour le prouver, faisons ceci:
Ici, la sortie ne sera pas définie. Pourquoi ? Dans ce cas, Javascript crée un objet String wrapper, définit une nouvelle propriété addNewProperty et rejette immédiatement l'objet wrapper. c'est pourquoi vous êtes indéfini. Le pseudo code ressemblerait à ceci:
la source
nous pouvons définir String de 3 façons
// nous pouvons également créer en utilisant 4. var d = a + '';
Vérifiez le type des chaînes créées à l'aide de l'opérateur typeof
quand on compare a et b var
a==b ( // yes)
lorsque vous comparez un objet String
la source